package com.gridnine.xtrip.server.reports.templates

import java.text.SimpleDateFormat
import java.util.List;

import com.gridnine.xtrip.common.Environment
import com.gridnine.xtrip.common.model.booking.BaseProduct
import com.gridnine.xtrip.common.model.booking.BookingFile
import com.gridnine.xtrip.common.model.booking.CommonProductIndex
import com.gridnine.xtrip.common.model.booking.GeneralProductFop;
import com.gridnine.xtrip.common.model.booking.ProductStatus
import com.gridnine.xtrip.common.model.dict.ContractType
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.handlers.HandlersRegistry
import com.gridnine.xtrip.common.model.handlers.ProductHandler
import com.gridnine.xtrip.common.model.helpers.BookingHelper
import com.gridnine.xtrip.common.model.helpers.GeneralProductHelper
import com.gridnine.xtrip.common.model.system.BillingTransactionCategory
import com.gridnine.xtrip.common.model.system.BillingTransactionIndex
import com.gridnine.xtrip.common.model.system.BillingTransactionType
import com.gridnine.xtrip.common.model.system.PaymentType
import com.gridnine.xtrip.common.search.SearchCriterion
import com.gridnine.xtrip.common.search.SearchQuery
import com.gridnine.xtrip.common.util.TextUtil
import com.gridnine.xtrip.common.vip.HiddenTaxHelper
import com.gridnine.xtrip.common.vip.HiddenTaxHelper.WrapBookingCallback
import com.gridnine.xtrip.common.model.profile.Organization;
import com.gridnine.xtrip.common.model.EntityReference;

import java.math.BigDecimal;

import com.gridnine.xtrip.common.model.booking.Reservation;
import com.gridnine.xtrip.common.model.dict.ProductCategory;
import com.gridnine.xtrip.common.model.dict.MCOCategory;
import com.gridnine.xtrip.common.model.booking.xtriphotels.HotelProduct
import com.gridnine.xtrip.common.model.system.BillingTransactionStatus

import com.gridnine.xtrip.common.model.booking.GeneralProductCommission
import com.gridnine.xtrip.common.model.booking.commission.DiscountProperties
import com.gridnine.xtrip.common.model.booking.commission.FeeProperties
import com.gridnine.xtrip.common.model.booking.commission.PaymentFeeProperties
import com.gridnine.xtrip.common.util.MiscUtil
import com.gridnine.xtrip.common.vip.CommonVIPHelper
import com.gridnine.xtrip.common.model.booking.vip.VipClientSitePriceStructure
import com.gridnine.xtrip.common.model.booking.vip.VipSubagentSitePriceStructure
//CREATING STYLES
createStyle(name: 'title',fontBold: true, h_span: 14, h_alignment: 'CENTER', v_alignment: 'CENTER', fontHeight:20)
createStyle(name: 'metadataTitle',fontBold: true, h_span: 2, h_alignment: 'RIGHT', v_alignment: 'CENTER', fontHeight:10)
createStyle(name: 'metadataValue', parent: 'metadataTitle', h_span: 10, fontBold: false, h_alignment: 'LEFT')
createStyle(name: 'metadataDateValue', parent: 'metadataValue', format: 'm/d/yy')
createStyle(name: 'topHeaderSale', h_span: 6, fontBold: true,  h_alignment: 'CENTER', v_alignment: 'CENTER', fontHeight:10, leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: 'topHeaderCompany', parent: 'topHeaderSale', h_span: 5)
createStyle(name: 'header',fontBold: true,  h_alignment: 'CENTER', v_alignment: 'CENTER', fontHeight:10, leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN', wrapText: true)
createStyle(name: 'header2', fontBold: true,  fontHeight:10, leftBorder:'THIN', rightBorder:'THIN', wrapText: true)
createStyle(name: 'data',fontBold: false,  h_alignment: 'CENTER', v_alignment: 'CENTER', fontHeight:10, leftBorder:'THIN', rightBorder:'THIN', topBorder:'THIN', bottomBorder:'THIN')
createStyle(name: 'textData',parent: 'data')
createStyle(name: 'numberData', parent: 'data', h_alignment: 'RIGHT')
createStyle(name: 'dateData', parent: 'data', h_alignment: 'RIGHT', format: 'm/d/yy')
createStyle(name: 'spaceData', parent: 'data', topBorder:'NONE', bottomBorder:'NONE')
createStyle(name: 'totalText', parent: 'header')
createStyle(name: 'totalNumber', parent: 'header', h_alignment: 'RIGHT')
    
//ADDITIONAL FUNCTIONS
def sumFormula = {List lst ->
    StringBuilder sb = new StringBuilder();
    int idx = 0;
    lst.each {
        if(idx > 0){
            sb.append('+')
        }
        sb.append(it)
        idx++
    }
    return sb.length()>0? sb.toString(): '0'
}

def notNull = {BigDecimal it ->
    return it == null? BigDecimal.ZERO: it
}

def cell = { value ->
    if (value instanceof Number)
        number(value, 'numberData')
    else if (value instanceof Date)
        date(value, 'dateData')
    else
        text(value, 'textData')
}

def formatDate = { value ->
    return value != null ? new SimpleDateFormat('dd.MM.yyyy').format(value) : ''
}

def printException = { Throwable error ->
    baos = new ByteArrayOutputStream();
    try {
        error.printStackTrace(new PrintStream(baos, true, "utf-8"));
        createStyle(name: 'error', fontBold: true, h_alignment: 'LEFT', v_alignment: 'TOP', fontHeight:10)
        nextRow()
        rowHeight(200)
        text(baos.toString(), 'error'); nextColumn()
    } finally {
        baos.close();
    }
}

def isSecondTable = { type ->
    return type == 'Продажа' || type == 'Пополнение'
}

def BigDecimal delta = new BigDecimal(0.001);

def isZero = { BigDecimal value ->
    return value.abs().compareTo(delta) <= 0;
}

def allProductsIteration = { BookingFile bookingFile, Closure<Boolean> continueFunc ->
    for (Reservation itReservation : bookingFile.reservations) {
        for (BaseProduct itBaseProduct : itReservation.products) {
            if (!continueFunc(itBaseProduct)) {
                return;
            }
        }
    }
}

def getProductHandler = { BaseProduct product ->
    return Environment.getPublished(HandlersRegistry.class).findProductHandler(product.getClass());
}

def isMcoPenalty = { ProductHandler<BaseProduct> handler, BaseProduct product ->
    ProductCategory category = handler.getProductCategory(product)
    if (category != ProductCategory.MCO) {
        return false;
    }
    MCOCategory mcoCategory = handler.getMCOCategory(product);
    return mcoCategory == MCOCategory.PENALTY;
}

def isRelatedTo = { ProductHandler<BaseProduct> handler, BaseProduct product, BaseProduct relatedProduct ->
    List<BaseProduct> related = handler.getRelatedProducts(product);
    if (related == null) {
        return false;
    }
    for (BaseProduct it : related) {
        if (it == relatedProduct) {
            return true;
        }
    }
    return false;
}

BigDecimal getHiddenFee(BaseProduct product) {
    ProductHandler handler = GeneralProductHelper.getHandler(product)
    ProductStatus status = handler.getStatus(product)
    if (status == ProductStatus.VOID || status == ProductStatus.VOID_BOOKING) {
        return BigDecimal.ZERO
    }
    List commissions = GeneralProductHelper.getUnmodifiableCommissions(product, null)
    BigDecimal result = BigDecimal.ZERO
    for (GeneralProductCommission commission : commissions) {
        if ((commission.commissionProperties != null) &&
            (FeeProperties.class == commission.commissionProperties.type || PaymentFeeProperties.class == commission.commissionProperties.type) &&
            (commission.contractType == ContractType.SUBAGENCY)) {
            result += commission.equivalentAmount;
        }
        if ((commission.getCommissionProperties() != null)
            && (DiscountProperties.class.equals(commission
                .getCommissionProperties().getType()))
            && (commission.getContractType() == ContractType.SUBAGENCY)) {
            result = result.subtract(commission.getEquivalentAmount());
        }
    }
}

try {

    def entityStorage = EntityStorage.get()

    //REPORT
    page { 'Отчёт' } {
        //warn 'reportVersion=' + '0.0.2'
        //title
        //metadata
        nextRow(); rowHeight(12);
        text('Дата составления отчета:', 'metadataTitle'); nextColumn();date(new Date(), 'metadataDateValue');nextRow();
        text('Период:', 'metadataTitle'); nextColumn();text(parameters.REPORT_PERIOD, 'metadataValue');nextRow();
        if (parameters.subagency != null) {
            text('Субагентство:', 'metadataTitle'); nextColumn();text(entityStorage.resolve(parameters.subagency).entity.fullName.toString(), 'metadataValue');nextRow();
        }
        int scw = 7
        //header
        2.times { nextRow() }
        rowHeight(12)
        text('Продажа', 'topHeaderSale'); nextColumn();
        text('', 'header2'); nextColumn();
        text('Баланс компании', 'topHeaderCompany');
        nextRow();
        setStyle('header')
        rowHeight(12)
        setStyle('header')
        columnWidth(2*scw);text('Операция'); nextColumn();
        columnWidth(2*scw);text('Дата'); nextColumn();
        columnWidth(2*scw);text('Заказ');nextColumn();
        columnWidth(2.5*scw);text('№ билета'); nextColumn();
        columnWidth(2*scw);text('Сумма'); nextColumn();
        columnWidth(2*scw);text('Сбор (ПРТБ)'); nextColumn();
        columnWidth(0.5*scw);text('', 'header2'); nextColumn();
        setStyle('header')
        columnWidth(2*scw);text('Операция'); nextColumn();
        columnWidth(2*scw);text('Дата'); nextColumn();
        columnWidth(2.5*scw);text('№ билета'); nextColumn();
        columnWidth(2*scw);text('Сумма'); nextColumn();
        columnWidth(2*scw);text('Сбор (ПРТБ)'); nextColumn();
        columnWidth(0.5*scw);text('', 'header2'); nextColumn();
        setStyle('header')
        columnWidth(4.5*scw);text('Ошибка');


        Date startingDate = parameters.params.periodBegin
        Date finishingDate = parameters.params.periodEnd

        def table = []
        def Set transactions = []
        def penaltyErrorRows = [] as Set

        tickets { CommonProductIndex idx ->
            def bookingFileContainer = entityStorage.resolve(idx.source)
            if (bookingFileContainer == null) {
                warn "Не найден заказ ${idx.bookingNumber}"
                return
            }

            final BookingFile bookingFile = bookingFileContainer.entity;
            
            final List<EntityReference<Organization>> blankOwners = parameters['BLANK_OWNERS'];
        
            HiddenTaxHelper.wrapAndExecute(bookingFile, new WrapBookingCallback<Void>() {
                @Override
                public Void process(final BookingFile booking) throws Exception {
                
                    def BaseProduct product = BookingHelper.findProduct(bookingFile, idx.navigationKey)
                    if (product == null) {
                        warn "Не найден продукт ${idx.systemNumbers} (${idx.navigationKey}), заказ ${idx.bookingNumber}"
                        return
                    }
        
                    def ProductHandler<BaseProduct> handler =
                            Environment.getPublished(HandlersRegistry.class)
                            .findProductHandler(product.getClass());
                    if (handler == null) {
                        warn "no product handler is registered for product class ${product.class.name}";
                        return
                    }
        
                    def type
                    def productStatus = handler.getStatus(product)
                    if (null == productStatus)
                        return
                    switch (productStatus) {
                        case ProductStatus.SELL:
                            type = 'Продажа'
                            break
                        case ProductStatus.REFUND:
                            type = 'Возврат'
                            break
                        case ProductStatus.EXCHANGE:
                            type = 'Обмен'
                            break
                        default:
                            return
                    }
        
                    def row = null
                    def fops
                    def numbers = null
                    boolean checkActiveTransactions = false
        
                    def issueDate = handler.findIssueDate(product)
                    if (product instanceof HotelProduct) {
                        HotelProduct hotelProduct = product
                        Date unholdDate = hotelProduct.unholdDate
                        if (unholdDate != null) {
                            issueDate = unholdDate
                            checkActiveTransactions = true
                        }
                    }
                    if (issueDate == null ||
                        (startingDate != null && issueDate.before(startingDate)) ||
                        (finishingDate != null && issueDate.after(finishingDate))
                    ) {
                        fops = null
                    } else {
                        if (row == null) {
                            row = []
                            if (blankOwners.empty || blankOwners.contains(idx.getBlankOwner())) {
                                table.add(row)
                            }
                        }
        
                        row.add(type)
                        row.add(formatDate(issueDate))
                        row.add(bookingFile.number)
                        def numbersList = handler.getProductNumbers(product);
                        numbers = numbersList != null && numbersList.size() > 0 ? numbersList.inject { acc, value -> "$acc, $value" } : ''
                        row.add(numbers)
        
                        def price = notNull(handler.calculateProductPrice(product, ContractType.CLIENT).getTotal());
                        
                        BigDecimal hiddenFee = BigDecimal.ZERO
                        if (product instanceof HotelProduct) {
                            HotelProduct hotelProduct = product
                            VipClientSitePriceStructure priceStructure = CommonVIPHelper.getClientPriceStructure(hotelProduct);
                            hiddenFee = MiscUtil.guarded(priceStructure.clientHiddenFees)
                        }
                        
                        if (productStatus == ProductStatus.REFUND || productStatus == ProductStatus.EXCHANGE)
                            price = price.negate()
                        price = MiscUtil.sum(price, hiddenFee)
                        row.add(price)
        
                        def subagencyFee = notNull(GeneralProductHelper.calculateFee(product, ContractType.SUBAGENCY)); /////////////////////////////////////////////////////////////
                        if (productStatus == ProductStatus.REFUND || productStatus == ProductStatus.EXCHANGE)
                            subagencyFee = subagencyFee.negate()
                        subagencyFee = subagencyFee - hiddenFee
                        row.add(subagencyFee)
        
                        fops = []
                        try { fops.addAll(product.getValue('subagentFops')) } catch (IllegalArgumentException e) { }
                        fops.addAll(GeneralProductHelper.getUnmodifiableFops(product, ContractType.SUBAGENCY))
                    }
        
                    def result = null
        
                    if (null != fops && fops.size() > 0) {
                        def query = new SearchQuery();
                        query.criteria.criterions.add(SearchCriterion.eq(BillingTransactionIndex.Property.referencedEntity.name(), bookingFileContainer.toReference()));
                        query.criteria.criterions.add(SearchCriterion.eq(BillingTransactionIndex.Property.contractType.name(), ContractType.SUBAGENCY));
                        if (productStatus == ProductStatus.SELL) {
                            query.criteria.criterions.add(SearchCriterion.eq(BillingTransactionIndex.Property.type.name(),  BillingTransactionType.EXPENSE));
                        } else {
                            boolean creditCardPayment = fops.any {
                                it.type == PaymentType.CREDIT_CARD_UNITELLER ||
                                it.type == PaymentType.CREDIT_CARD_SPLIT_UNITELLER ||
                                it.type == PaymentType.CREDIT_CARD_ONLINE_SIRENA ||
                                it.type == PaymentType.CREDIT_CARD_SUPPLIER_SIRENA ||
                                it.type == PaymentType.CREDIT_CARD_AGENCY_SIRENA ||
                                it.type == PaymentType.CREDIT_CARD_SUPPLIER_UNITELLER ||
                                it.type == PaymentType.CREDIT_CARD_AGENCY_UNITELLER
                            }
                            if(productStatus == ProductStatus.REFUND && creditCardPayment){
                                
                                query.criteria.criterions.add(SearchCriterion.eq(BillingTransactionIndex.Property.type.name(),  BillingTransactionType.REPAYMENT));
                                query.criteria.criterions.add(SearchCriterion.eq(BillingTransactionIndex.Property.category.name(),  BillingTransactionCategory.DEBIT));
                            } else {
                            
                                query.criteria.criterions.add(SearchCriterion.not(SearchCriterion.eq(BillingTransactionIndex.Property.type.name(),  BillingTransactionType.EXPENSE)));
                            }
                        }
                        
                        if (checkActiveTransactions) {
                            query.criteria.criterions.add(SearchCriterion.eq(BillingTransactionIndex.Property.status.name(), BillingTransactionStatus.ACTIVE))
                        }
        
                        def criterions = fops.collect() { fop -> SearchCriterion.eq(BillingTransactionIndex.Property.fopUid.name(), fop.uid) }
                        query.criteria.criterions.add(SearchCriterion.or(criterions.toArray(new SearchCriterion[criterions.size()])));
                        
                        result = entityStorage.search(BillingTransactionIndex, query)
                    }
        
                    if (null == result || 0 == result.data.size()) {
                        if (row != null)
                            5.times { row.add(null) }
                    } else {
                        def type2 = null
                        def Set date = []
                        def sum = BigDecimal.ZERO;
                        def fee = BigDecimal.ZERO;
        
                        def p = false;
        
                        result.data.each { BillingTransactionIndex tran ->
                            
                            def fop = fops.find { it.uid == tran.fopUid }
        
                            if (fop == null)
                                return
        
                            def tranDate = tran.transactionDate
        
                            if (tranDate == null ||
                                (startingDate != null && tranDate.before(startingDate)) ||
                                (finishingDate != null && tranDate.after(finishingDate))
                            )
                                return
        
                            p = true;
        
                            transactions.add(tran.source.uid)
                            
        
                            def isCommission = !GeneralProductHelper.isServiceFop(fop)
        
                            if (tran.transactionSum != null)
                                if (!isCommission)
                                    sum = sum.add(tran.transactionSum)
                                else
                                    fee = fee.add(tran.transactionSum)
        
                            date.add(tranDate)
        
                            def type2Name = ''
                            switch (tran.type) {
                                case BillingTransactionType.EXPENSE:
                                    type2Name = 'Продажа'
                                    break
                                case BillingTransactionType.REPAYMENT:
                                    if (productStatus == ProductStatus.EXCHANGE)
                                        type2Name = 'Обмен'
                                    else
                                        type2Name = 'Возврат'
                                    break
                            }
                            if (type2 == null)
                                type2 = type2Name
                            else
                            if (type2 != type2Name)
                                type2 = ''
                        }
        
                        if (!p) {
                            if (row != null)
                                5.times { row.add(null) }
                        } else {
                            if (row == null) {
                                row = []
                                table.add(row)
                                5.times { row.add(null) }
                            }
        
                            row.add(type2)
                            row.add(date.size() > 0 ? date.sort { it }.collect() { formatDate(it) }.inject { acc, value -> "$acc, $value" } : null)
                            row.add(numbers)
                            row.add(sum)
                            row.add(fee)
                        }
                    }
                    
                    boolean penaltyError = false;
                    if (row != null && productStatus == ProductStatus.REFUND) {
                        BigDecimal penalty = handler.getPenalty(product);
                        if (penalty != null && !isZero(penalty)) {
                            def iteration = { BaseProduct itProduct ->
                                ProductHandler<BaseProduct> itHandler = getProductHandler(itProduct);
                                if (isMcoPenalty(itHandler, itProduct) && isRelatedTo(itHandler, itProduct, product)) {
                                    penaltyError = true;
                                    return false;
                                }
                                return true;
                            }
                            allProductsIteration(bookingFile, iteration);
                        }
                    }
                    if (penaltyError) {
                        penaltyErrorRows.add(row);
                    }
                    
                    return null
                }
            });
        }

        // two pass
        def query = new SearchQuery();
        //query.criteria.criterions.add(SearchCriterion.eq(BillingTransactionIndex.Property.referencedEntity.name(), bookingFileContainer.toReference()));
        query.criteria.criterions.add(SearchCriterion.eq(BillingTransactionIndex.Property.contractType.name(), ContractType.SUBAGENCY));
        if (parameters.subagency != null)
            query.criteria.criterions.add(SearchCriterion.eq(BillingTransactionIndex.Property.client.name(), parameters.subagency));
        if (!parameters.agency.empty){
            List<SearchCriterion> agencyCriterions = parameters.agency.collect() { EntityReference<Organization> agentRef ->
                SearchCriterion.eq(BillingTransactionIndex.Property.agency.name(), agentRef);
            }
            query.criteria.criterions.add(SearchCriterion.or(agencyCriterions.toArray(new SearchCriterion[0])));
            //query.criteria.criterions.add(SearchCriterion.eq(BillingTransactionIndex.Property.agency.name(), parameters.agency));
        }
        if (startingDate != null)
            query.criteria.criterions.add(SearchCriterion.ge(BillingTransactionIndex.Property.transactionDate.name(), startingDate));
        if (finishingDate != null)
            query.criteria.criterions.add(SearchCriterion.le(BillingTransactionIndex.Property.transactionDate.name(), finishingDate));
        query.criteria.criterions.add(SearchCriterion.not(
                SearchCriterion.and(
                SearchCriterion.eq(BillingTransactionIndex.Property.paymentType.name(), PaymentType.TERMINAL_TOURPAY),
                SearchCriterion.eq(BillingTransactionIndex.Property.category.name(), BillingTransactionCategory.DEBIT),
                SearchCriterion.eq(BillingTransactionIndex.Property.type.name(), BillingTransactionType.BILLING)
                )
                ))
        query.criteria.criterions.add(SearchCriterion.eq(BillingTransactionIndex.Property.type.name(),  BillingTransactionType.EXPENSE));
        query.criteria.criterions.add(SearchCriterion.ne(BillingTransactionIndex.Property.status.name(), BillingTransactionStatus.HOLD))

        def result = entityStorage.search(BillingTransactionIndex, query)

        result.data.each { BillingTransactionIndex tran ->
            if (transactions.contains(tran.source.uid))
                return

            def tranDate = tran.transactionDate

            if (tranDate == null ||
                (startingDate != null && tranDate.before(startingDate)) ||
                (finishingDate != null && tranDate.after(finishingDate))
            )
                return
                
            if (tran.referencedEntity?.type != BookingFile.class) {
                return
            }

            def type2 = null

            switch (tran.type) {
                case BillingTransactionType.EXPENSE:
                    type2 = 'Продажа'
                    break
                case BillingTransactionType.REPAYMENT:
                    type2 = 'Обмен/Возврат'
                    break
                default:
                    if (tran.type != null)
                        type2 = tran.type.toString()
            }

            def ticketNumber = tran.referencedEntity != null ? "Заказ ${tran.referencedEntity.toString()}" : null

            row = []
            table.add(row)
            6.times { row.add(null) }
            row.add(type2)
            row.add(formatDate(tranDate))
            row.add(ticketNumber)
            row.add(tran.transactionSum)
            row.add(null)
        }

        table.sort { row1, row2 ->
            def d = (isSecondTable(!TextUtil.isBlank(row1[0]) ? row1[0] : row1[6])) <=> (isSecondTable(!TextUtil.isBlank(row2[0]) ? row2[0] : row2[6]))
            if (d != 0)
                return d
            d = (!TextUtil.isBlank(row1[1]) ? row1[1] : row1[7])  <=> (!TextUtil.isBlank(row2[1]) ? row2[1] : row2[7])
            if (d != 0)
                return d
            return (!TextUtil.isBlank(row1[3]) ? row1[3] : row1[8]) <=> (!TextUtil.isBlank(row2[3]) ? row2[3] : row2[8])
        }

        def totalL = BigDecimal.ZERO
        def totalR = BigDecimal.ZERO
        def firstTable = true

        table.each { row ->
            if (firstTable && (isSecondTable(!TextUtil.isBlank(row[0]) ? row[0] : row[6]))) {
                firstTable = false
                nextRow()
                rowHeight(12)
                text('ИТОГО', 'totalText'); nextColumn()
                3.times { text(null); nextColumn() }
                number(totalL, 'totalNumber'); nextColumn()
                text(null); nextColumn()
                text(null, 'spaceData'); nextColumn()
                setStyle('header')
                3.times { text(null); nextColumn() }
                number(totalR, 'totalNumber'); nextColumn()
                text(null);

                totalL = BigDecimal.ZERO
                totalR = BigDecimal.ZERO

                nextRow()
                rowHeight(12)
            }

            if (row[4] instanceof BigDecimal)
                totalL = totalL.add(row[4])
            if (row[9] instanceof BigDecimal)
                totalR = totalR.add(row[9])

            nextRow()
            rowHeight(12)

            row.eachWithIndex { value, index ->
                cell(value)
                nextColumn()

                if (index == 5 || index == 10) {
                    text('', 'spaceData')
                    nextColumn()
                }
            }

            def zeroOrder = row[4] == 0 && row[5] == 0
            def boolean isError = row[0] != (null != row[6] || !zeroOrder ? row[6] : row[0]) ||
                    row[1] != (null != row[7] || !zeroOrder ? row[7] : row[1]) ||
                    row[3] != (null != row[8] || !zeroOrder ? row[8] : row[3]) ||
                    row[4] != notNull(row[9]) ||
                    row[5] != notNull(row[10]);
            def isPenaltyError = penaltyErrorRows.contains(row);
            cell(isPenaltyError ? "Ошибка - присутствует лишний штраф" : isError ? 'Ошибка' : '')
            nextColumn()
        }
        nextRow()
        rowHeight(12)
        text('ИТОГО', 'totalText'); nextColumn()
        3.times { text(null); nextColumn() }
        number(totalL, 'totalNumber'); nextColumn()
        text(null); nextColumn()
        text(null, 'spaceData'); nextColumn()
        setStyle('header')
        3.times { text(null); nextColumn() }
        number(totalR, 'totalNumber'); nextColumn()
        text(null);

    }
} catch (Throwable t) {
    //printException t
    error 'Ошибка при формировании отчёта: ' + t.message, t
    throw t
}

warn 'reportVersion=' + '0.0.3'