//отчет по продажам отелей 20170824
import java.text.SimpleDateFormat

import org.apache.commons.lang.StringUtils

import com.gridnine.xtrip.common.Environment
import com.gridnine.xtrip.common.l10n.model.L10nStringHelper
import com.gridnine.xtrip.common.l10n.model.LocaleHelper
import com.gridnine.xtrip.common.model.EntityContainer
import com.gridnine.xtrip.common.model.EntityReference
import com.gridnine.xtrip.common.model.booking.*
import com.gridnine.xtrip.common.model.booking.commission.CommissionProperties
import com.gridnine.xtrip.common.model.booking.commission.DiscountProperties
import com.gridnine.xtrip.common.model.booking.vip.VipSubagentSitePriceStructure
import com.gridnine.xtrip.common.model.booking.xtriphotels.HotelProduct
import com.gridnine.xtrip.common.model.booking.xtriphotels.HotelProductContractRelationData
import com.gridnine.xtrip.common.model.booking.xtriphotels.HotelProductFop
import com.gridnine.xtrip.common.model.booking.xtriphotels.MainHotelProductIndex
import com.gridnine.xtrip.common.model.booking.xtriphotels.Penalty
import com.gridnine.xtrip.common.model.booking.xtriphotels.Room
import com.gridnine.xtrip.common.model.dict.ContractType
import com.gridnine.xtrip.common.model.dict.DictionaryCache
import com.gridnine.xtrip.common.model.dict.GeoLocation
import com.gridnine.xtrip.common.model.dict.PreferenceKey;
import com.gridnine.xtrip.common.model.entity.EntityStorage
import com.gridnine.xtrip.common.model.booking.ProductStatus
import com.gridnine.xtrip.common.model.helpers.*
import com.gridnine.xtrip.common.model.profile.Organization
import com.gridnine.xtrip.common.model.profile.Person
import com.gridnine.xtrip.common.model.vip.booking.HistoryEntry
import com.gridnine.xtrip.common.search.SearchCriterion
import com.gridnine.xtrip.common.search.SearchQuery
import com.gridnine.xtrip.common.util.MiscUtil;
import com.gridnine.xtrip.common.vip.CommonVIPHelper

import groovy.transform.Field

processIncomingData()
createSpreadSheetStyle()
composeReport()

class Hotel {
    String agencyName
    String subagentName
    String clientName
    String agent
    String group
    ProductStatus productStatus
    String issueDate
    String checkIn
    String checkOut
    String unholdDate
    String hotelCountry
    String hotelCity
    String hotelName
    String travellersName
    String currencyCode
    String reservationNumber
    String blankOwner
    String bookingNumberPortbilet
    BigDecimal supplierFee
    String formTax
    BigDecimal agencyCommision
    BigDecimal vipCommision
    String supplier
    BigDecimal vipFee
    BigDecimal totalFee
    BigDecimal toPaid
    String onHold
    BigDecimal vat = BigDecimal.ZERO
    BigDecimal penalty = BigDecimal.ZERO
    boolean isRefund = false
    String actionSources
}

class Aggregator {
    BigDecimal supplierFee = BigDecimal.ZERO
    BigDecimal vipFee = BigDecimal.ZERO
    BigDecimal totalFee = BigDecimal.ZERO
    BigDecimal agencyCommision = BigDecimal.ZERO
    BigDecimal vipCommision = BigDecimal.ZERO
    BigDecimal toPaid = BigDecimal.ZERO
    BigDecimal vat = BigDecimal.ZERO
    BigDecimal penalty = BigDecimal.ZERO
    
    void append(Hotel hotel) {
        supplierFee = MiscUtil.sum(supplierFee, hotel.supplierFee)
        vipFee = MiscUtil.sum(vipFee, hotel.vipFee)
        totalFee = MiscUtil.sum(totalFee, hotel.totalFee)
        agencyCommision = MiscUtil.sum(agencyCommision, hotel.agencyCommision)
        vipCommision = MiscUtil.sum(vipCommision, hotel.vipCommision)
        toPaid = MiscUtil.sum(toPaid, hotel.toPaid)
        vat = MiscUtil.sum(vat, hotel.vat)
        penalty = MiscUtil.sum(penalty, hotel.penalty)
    }
}

class AggregatorByStatus {
    Map byStatus = [:]
    Aggregator total = new Aggregator()
    
    void append(Hotel hotel) {
        Aggregator statusAggregator
        if (byStatus.containsKey(hotel.productStatus)) {
            statusAggregator = byStatus[hotel.productStatus]
        } else {
            statusAggregator = new Aggregator()
            byStatus[hotel.productStatus] = statusAggregator
        }
        statusAggregator.append(hotel)
        if (hotel.productStatus != ProductStatus.BOOKING && hotel.productStatus != ProductStatus.VOID_BOOKING &&
            hotel.productStatus != ProductStatus.REQUEST && hotel.productStatus != ProductStatus.REJECT) {
            total.append(hotel)
        }
    }
}

void printAggregator(String title, Aggregator aggregator) {
    processNextCell(1, isByDepartureDate ? 1 : 2) {
        text(title, 'preliminaryTotalText')
    }
    processNextCell(0, 1) { number(aggregator.supplierFee, 'totalNumber') }
    processNextCell(0, 1) { number(aggregator.vipFee, 'totalNumber') }
    processNextCell(0, 1) { number(aggregator.totalFee, 'totalNumber') }
    processNextCell(0, 1) { number(aggregator.penalty, 'totalNumber') }
    processNextCell(0, 1) { number(aggregator.agencyCommision, 'totalNumber') }
    processNextCell(0, 1) { number(aggregator.vipCommision, 'totalNumber') }
    processNextCell(0, 1) { number(aggregator.toPaid, 'totalNumber') }
    processNextCell(0, 1) { number(aggregator.vat, 'totalNumber') }
}

void printAggregator(String group, AggregatorByStatus aggregator) {
    for (Map.Entry entry : aggregator.byStatus.entrySet()) {
        if (entry.key == ProductStatus.REQUEST || entry.key == ProductStatus.REJECT) {
            continue
        }
        printAggregator("Итого операции ${entry.key} по ${group}:", entry.value)
    }
    printAggregator("Итого по ${group}:", aggregator.total)
}

@Field private String specifiedProfileGroup = ''
@Field private String specifiedSubAgents = ''
@Field private String specifiedAgents = ''
@Field private String specifiedGds = ''
@Field private boolean isGroupedByAgent = false
@Field private boolean isGroupedByProfileGroup = false
@Field private boolean isByDepartureDate = false
@Field SimpleDateFormat dateFormat = new SimpleDateFormat("dd.MM.yyyy")
@Field boolean holdPeriod = false

private void createSpreadSheetStyle() {
    createStyle(name: 'title', fontBold: true, h_span: getColumnShiftTotal(20),
    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: 'header', fontBold: false, h_alignment: 'CENTER',
    v_alignment: 'CENTER', fontHeight: 10,
    leftBorder: 'MEDIUM', rightBorder: 'MEDIUM',
    topBorder: 'MEDIUM', bottomBorder: 'MEDIUM',
    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',
    format: '#,##0.00')
    createStyle(name: 'preliminaryTotalText', parent: 'data',
    h_span: getColumnShiftTotal(17), fontBold: true,
    h_alignment: 'RIGHT')
    createStyle(name: 'finalTotalText', parent: 'data', h_span: 2,
    fontBold: true, h_alignment: 'RIGHT')
    createStyle(name: 'totalNumber', fontBold: true, parent: 'numberData')
}

private String getSpecifiedSubAgents() {
    List objectList = parameters['SUBAGENT']
    String result = StringUtils.join(new ArrayList() { {
                    objectList.each {
                        def object = EntityStorage.get().resolve(it)?.getEntity()
                        add(ProfileHelper.getFullName(object,
                        LocaleHelper.getCurrentLocale(),
                        false))
                    }
                }
            }, ', ')
    return result
}

private String getSpecifiedProfileGroup() {
    def group = parameters['PROFILE_GROUP']
    return group == null ? '' : group.toString()
}

private String getSpecifiedAgents() {
    List objectList = parameters['AGENT']
    String result = StringUtils.join(new ArrayList() { {
                    objectList.each {
                        def object = EntityStorage.get().resolve(it)?.getEntity()
                        add(ProfileHelper.getFullName(object,
                        LocaleHelper.getCurrentLocale(),
                        false))
                    }
                }
            }, ', ')
    return result
}




private String getSpecifiedGds() {
    def gds = parameters['RESERVATION_SYSTEM']
    return gds == null ? '' : gds.toString()
}

private boolean isGroupedByAgent() {
    return parameters['GROUP_BY_AGENT']
}

private boolean isGroupedByProfileGroup() {
    return parameters['GROUP_BY_PROFILE_GROUP']
}

@Field boolean isShortcut

private void processIncomingData() {
    this.specifiedSubAgents = getSpecifiedSubAgents()
    this.specifiedProfileGroup = getSpecifiedProfileGroup()
    this.specifiedAgents = getSpecifiedAgents()
    this.specifiedGds = getSpecifiedGds()
    this.isGroupedByAgent = isGroupedByAgent()
    this.isGroupedByProfileGroup = isGroupedByProfileGroup()
    this.holdPeriod = parameters['HOLDED']
    this.isByDepartureDate = !holdPeriod && parameters['BY_DEPARTURE_DATE']
    this.isShortcut = !isByDepartureDate && parameters['IS_SHORTCUT']
}

class ProductInfo implements Comparable<ProductInfo> {
    MainHotelProductIndex index
    HotelProduct product
    BookingFile bookingFile
    String clientName;

    @Override
    public int compareTo(ProductInfo other) {
		if (product == null) {
			return -1;
		}
		if (product.getIssueDate() == null) {
			return -1;
		}
		if (other == null) {
			return 1;
		}
		if (other.product == null) {
			return 1;
		}
		if (other.product.getIssueDate() == null) {
			return 1;
		}
        return product.getIssueDate().compareTo(other.product.getIssueDate());
    }
}

private HotelProductFop transformProductFop(GeneralProductFop fop) {
    HotelProductFop result = new HotelProductFop()
    result.uid = fop.uid
    result.agent = fop.agent
    result.card = fop.card
    if (fop.equivalentAmount != null) {
        result.amount = SystemHelper.getMoney(
            fop.equivalentAmount.doubleValue(),
            DictHelper.getPreferenceValue(PreferenceKey.EQUIVE_CURRENCY, "RUB")
        )
    }
    result.operationDate = fop.operationDate
    result.relatedTicketNumber = fop.relatedTicketNumber
    result.type = fop.type
    result.payer = fop.payer
    result.commissions.addAll(fop.commissions)
    return result
}

private HotelProductContractRelationData transformContractRelationData(GeneralProductContractRelationData data) {
    HotelProductContractRelationData transformedData = new HotelProductContractRelationData()
    transformedData.uid = data.uid
    transformedData.description = data.description
    transformedData.commissions.addAll(data.commissions)
    transformedData.fops.addAll(data.fops.collect() { GeneralProductFop fop ->
        transformProductFop(fop)
    })
    transformedData.setValue("generalData", data.generalData)
    transformedData.setValue("serviceData", data.serviceData)
    return transformedData
}

private Date getCheckDate(HotelProduct product) {
    if (product.status == ProductStatus.SELL) {
        try {
            return HotelProductHelper.getLastCheckoutDate(product)
        } catch (Exception e) {
            return null
        }
    } else if (product.status == ProductStatus.REFUND) {
        return product.issueDate
    }
}

BigDecimal getPenalty(HotelProduct product) {
    if (product.status != ProductStatus.REFUND) {
        return BigDecimal.ZERO
    }
    HotelProduct sellProduct = product.previousProduct
    VipSubagentSitePriceStructure productPriceStructure = CommonVIPHelper.getSubagentPriceStructure(product);
    VipSubagentSitePriceStructure sellProductPriceStructure = CommonVIPHelper.getSubagentPriceStructure(sellProduct);
    BigDecimal result = MiscUtil.sum(productPriceStructure?.price?.negate(), sellProductPriceStructure?.price)    
    if (result == null) {
        return BigDecimal.ZERO
    }
    return result
}

boolean hasPenalty(HotelProduct product) {
    boolean hasPenalty = false
    for (Penalty penalty : product.penalties) {
        BigDecimal penaltyAmount = penalty?.equivalentAmount
        if (penaltyAmount != null && penaltyAmount.compareTo(BigDecimal.ZERO) > 0) {
            hasPenalty = true
            break
        }
    }
    if (hasPenalty) {
        return true
    }
    return product.cancellationCharges != null && !product.cancellationCharges.empty
}

boolean excludeFullRefund(HotelProduct product){
    def result  = false
    def nextProd = product?.nextProduct
    def prevProd = product?.previousProduct
    def prodPrice = CommonVIPHelper.getSubagentPriceStructure(product).price
    def prevProdPrice = (prevProd)? CommonVIPHelper.getSubagentPriceStructure(prevProd).price: null
    def nextProdPrice = (nextProd)?CommonVIPHelper.getSubagentPriceStructure(nextProd).price:null
    
    if (product.status==ProductStatus.SELL 
        && nextProd
        && nextProd.status==ProductStatus.REFUND
        && prodPrice 
        && nextProdPrice
        && (nextProdPrice-prodPrice==0)        
        ){
       result = true; 
        
    }
    
    if (product.status==ProductStatus.REFUND 
        && prevProd
        && prevProd.status==ProductStatus.SELL
        && prodPrice
        && prevProdPrice
        && (prevProdPrice- prodPrice==0)
        ){
            result = true;
    
    }
    return result    
}

private HotelProduct getHotelProduct(BookingFile bookingFile, String productNavigationKey) {
    BaseProduct baseProduct = BookingHelper.findProductByUid(productNavigationKey, bookingFile)
    HotelProduct product
    if (baseProduct instanceof HotelProduct) {
        product = baseProduct
    } else {
        warn "baseProduct ${baseProduct} is not hotel product"
        return null
    }   
    if (holdPeriod) {
        Date unholdDate = getUnholdDate(product)
        if (unholdDate == null) {
            return null
        }
        if (isAfter(unholdDate, parameters['params'].periodBegin) || isAfter(parameters['params'].periodEnd, unholdDate)) {
            return null
        }
    } else if (isByDepartureDate || isShortcut) {
        if ((product.status == ProductStatus.SELL || product.status == ProductStatus.REFUND) && product.onHold) {
            return null
        }
        if (product.status == ProductStatus.REFUND) {
            if (!hasPenalty(product)) {
                return null
            }
        }
        
        if (excludeFullRefund(product)){
           return null
        }
        
     /*   Date checkDate = getCheckDate(product)
        if(checkDate == null || checkDate.compareTo(MiscUtil.clearTime(parameters['params'].periodBegin)) < 0 || checkDate.compareTo(MiscUtil.setDayEndTime(parameters['params'].periodEnd)) > 0) {
            return null
        }*/
    }
    
    return product
}

Date getUnholdDate(HotelProduct product) {
    Date result
    if (product.unholdDate != null) {
        result = product.unholdDate
    } else {
        result = getLastCheckoutDate(product)
    }
    return MiscUtil.clearTime(result)
}

public static Date getLastCheckoutDate(final HotelProduct prod) {
    if (prod.getRooms().size() == 0) {
        return null;
    }
    Date result = new Date(Long.MIN_VALUE);
    for (Room room : prod.getRooms()) {
        if (room.getCheckOutDate() != null && room.getCheckOutDate().after(result)) {
            result = room.getCheckOutDate();
        }
    }
    if (result.getTime() == Long.MIN_VALUE) {
        return null;
    }
    return result;
}

boolean isAfter(Date prevDate, Date afterDate) {
    return afterDate.compareTo(MiscUtil.clearTime(prevDate)) > 0
}



List<ProductInfo> getProductInfos() {
    List<ProductInfo> result = new ArrayList<ProductInfo>()
  
    tickets.each { MainHotelProductIndex index -> 
         def bookingFile =  EntityStorage.get().resolve(index.source)?.entity
        //println "bookingNumber = ${index.bookingNumber}"
     /*  if (index.bookingNumber!='7092630'){
            return
        }*/
        
        if (!bookingFile) {
            return
        }
        HotelProduct product = getHotelProduct(bookingFile, index.navigationKey)
        if (!product) {
            return
        }
        ProductInfo productInfo = new ProductInfo();
        productInfo.product = product
        productInfo.bookingFile = bookingFile
        productInfo.clientName = index.client?.toString();
        productInfo.index = index
        result.add(productInfo)
    }
    Collections.sort(result)
    return result
}

class DayHotelSalesInfo {
    
    Set<String> saleBookingNumbers = [] as Set
    Set<String> refundBookingNumbers = [] as Set
    
    BigDecimal salePriceSum = BigDecimal.ZERO
    BigDecimal saleSubagencyCommissionSum = BigDecimal.ZERO
    BigDecimal saleVendorCommissionSum = BigDecimal.ZERO
    BigDecimal refundPriceSum = BigDecimal.ZERO
    BigDecimal refundSubagencyCommissionSum = BigDecimal.ZERO
    BigDecimal refundVendorCommissionSum = BigDecimal.ZERO
    
    Set<String> subagencyUids = [] as Set
}

@Field BigDecimal vatMutiplicator = BigDecimal.valueOf(0.152542)

String composeActionSources(BookingFile bookingFile) {
    final Set<String> sources = new HashSet<String>();
    final List<HistoryEntry> history = bookingFile.getHistory();
    for (HistoryEntry historyEntry : history) {
        if (historyEntry.getActionSource() != null) {
            sources.add(historyEntry.getActionSource().toString())
        }
    }
    final StringBuilder result = new StringBuilder()
    for (final String src : sources) {
        if (result.length() > 0) {
            result.append(", ")
        }
        result.append(src)
    }
    return result
}


BigDecimal calcVipFee(def product){
    VipSubagentSitePriceStructure subagentPriceStructure = CommonVIPHelper.getSubagentPriceStructure(product)
   /* if (product?.priceStructureSubagent){
        
    } else {
        subagentPriceStructure = CommonVIPHelper.getSubagentPriceStructure(product)
    }*/
    
    BigDecimal vipFee = subagentPriceStructure.portbiletFeesSum + subagentPriceStructure.portbiletHiddenFeesSum
    if (product.status == ProductStatus.REFUND) {
        vipFee = -vipFee
    }
    return vipFee    
}

String getProfileGroup(Map<String, String> profileGroups, MainHotelProductIndex index){
    String res =""
    if ((isGroupedByProfileGroup ||
            !specifiedProfileGroup.isEmpty())&&profileGroups) {
            def group = profileGroups.get(index.navigationKey)   
            if (group)  res =  group;           
    }        
    return res 
}

 Map<String, String>  getProfilesGroups(List<ProductInfo> products){
    final Map<String, String>  res = new HashMap<>()
    if (isGroupedByProfileGroup ||
        !specifiedProfileGroup.isEmpty()) {
        
       if (!products)  return
        
      List<String> keys =   products.collect {
            it.index.navigationKey
        }
        
      SearchQuery query = new SearchQuery();
      query.getPreferredProperties().add("navigationKey");
      query.getPreferredProperties().add(CommonProductIndex.Property.profileGroup.name());      
      query.getCriteria().getCriterions().add( SearchCriterion.in("navigationKey", keys.toArray()))
            
       
      List<CommonProductIndex> searchResult =
       EntityStorage.get()
           .search(CommonProductIndex.class, query)
           .getData();
           
     searchResult.each { commonIdx->
         res.put(commonIdx.navigationKey, commonIdx.profileGroup)
     }
        
    }    
    return res
} 




private void composeReport() {
    List<ProductInfo> products = getProductInfos()
    def profileGroups = getProfilesGroups(products)
    
    if (!isShortcut) {
        def hotelList = []

        products.each { ProductInfo productInfo ->
            Hotel hotel = new Hotel()
            HotelProduct product = productInfo.product
            BookingFile bookingFile = productInfo.bookingFile            
           /* CommonProductIndex commonIndex = productInfo.index
            String profileGroup = commonIndex?.getProfileGroup()?.toString()*/
            
            String profileGroup = getProfileGroup(profileGroups, productInfo.index)
            /*if (profileGroup == null) {
                profileGroup = ""
            }*/
            
         
            hotel.vipFee = calcVipFee(product)

            EntityReference<Organization> agencyRef = HotelProductHelper.getAgency(product)
            populateAgency(hotel, agencyRef)

            populateClient(hotel, productInfo)

            EntityReference<Organization> subagencyRef = HotelProductHelper.getSubagency(product)
            Reservation reservation = product.getReservation()
            boolean negate = false
            if ((product.getStatus() == ProductStatus.REFUND) ||
                    (product.getStatus() == ProductStatus.EXCHANGE) || product.status == ProductStatus.VOID_BOOKING) {
                negate = true
            }
            populateTaxForm(subagencyRef, hotel)
            populateSubAgent(hotel, subagencyRef)
            if (isGroupedByAgent || !specifiedAgents.isEmpty()) {
                populateAgent(reservation, hotel)
            }
            if (isGroupedByProfileGroup ||
            !specifiedProfileGroup.isEmpty()) {
                populateProfileGroup(profileGroup, hotel)
            }
            populateProductStatus(product, hotel)
            populateIssueDate(product, hotel)
            populateCheckInDate(product, hotel)
            populateUnholdDate(product, hotel)
            populateCheckOutDate(product, hotel)
            populateHotelCountry(product, hotel)
            populateHotelCity(product, hotel)
            populateHotelName(hotel, product)
            populateTravellersNames(product, hotel)
            populateCurrencyCode(hotel)
            populateReservationNumber(reservation, hotel)
            populatePrtbBookingNumber(bookingFile, hotel)
            populateSupplierFee(product, negate, hotel)
            populateSubAgencyComission(product, negate, hotel)
            populateVendorCommission(product, negate, hotel)
            populateTotalFee(product, hotel);
            populateBlankOwner(product, hotel)
            populateSupplier(product, hotel)
            populateToPaid(product, hotel)
            populateOnHold(product, hotel)
            populateVat(product, hotel)
            hotel.actionSources = composeActionSources(bookingFile)
            if (product.status == ProductStatus.SELL && product.nextProduct?.status == ProductStatus.REFUND) {
                hotel.isRefund = true
                hotel.penalty = getPenalty(product.nextProduct)
                hotel.vat = BigDecimal.ZERO
                hotel.toPaid = hotel.penalty
            }
            hotelList.add(hotel)
        }
        
        page { 'Отели' } {
            fillSpreadSheetHeader()
            fillTableHeader()
            fillTable(hotelList)
            fillTableFooter()
        }
    } else {
        
        Map<Date, DayHotelSalesInfo> dayHotelSalesInfos = [:]
        for (Date day = MiscUtil.clearTime(parameters['params'].periodBegin);
                day.time <= parameters['params'].periodEnd.time;
                day = new Date(day.time + 24 * 60 * 60 * 1000)) {
            dayHotelSalesInfos[day] = new DayHotelSalesInfo()
        }
        
        Hotel hotel = new Hotel()
        products.each { ProductInfo productInfo ->
            HotelProduct product = productInfo.product
            BookingFile bookingFile = productInfo.bookingFile
            Date day = MiscUtil.clearTime(productInfo.index.hotelCheckOutDate)
            DayHotelSalesInfo salesInfo = dayHotelSalesInfos[day]
            
            
            
            boolean negate = product.status == ProductStatus.REFUND || product.status == ProductStatus.EXCHANGE
            populateTotalFee(product, hotel)
            populateSubAgencyComission(product, negate, hotel)
            
            hotel.vipFee = calcVipFee(product)
            
            populateVendorCommission(product, negate, hotel)
            if (product.status == ProductStatus.SELL) {
                if (hotel.totalFee != null) {
                    salesInfo.salePriceSum = salesInfo.salePriceSum.add(hotel.totalFee)
                }
                if (hotel.agencyCommision != null) {
                    salesInfo.saleSubagencyCommissionSum = salesInfo.saleSubagencyCommissionSum.add(hotel.agencyCommision)
                }
                salesInfo.saleVendorCommissionSum = MiscUtil.sum(salesInfo.saleVendorCommissionSum, hotel.vipCommision, hotel.vipFee)
                salesInfo.saleBookingNumbers.add(bookingFile.number)
            } else if (product.status == ProductStatus.REFUND) {
                if (hotel.totalFee != null) {
                    salesInfo.refundPriceSum = salesInfo.refundPriceSum.add(hotel.totalFee)
                }
                if (hotel.agencyCommision != null) {
                    salesInfo.refundSubagencyCommissionSum = salesInfo.refundSubagencyCommissionSum.add(hotel.agencyCommision)
                }
                salesInfo.refundVendorCommissionSum = MiscUtil.sum(salesInfo.refundVendorCommissionSum, hotel.vipCommision, hotel.vipFee)
                salesInfo.refundBookingNumbers.add(bookingFile.number)
            } else {
                return
            }
            EntityReference<Organization> subagencyRef = HotelProductHelper.getSubagency(product)
            if (subagencyRef != null) {
                salesInfo.subagencyUids.add(subagencyRef.uid)
            }
        }
        page { "Отели" } {
            printShortcutReportHeader()
            for (Date day = MiscUtil.clearTime(parameters['params'].periodBegin);
                    day.getTime() <= parameters['params'].periodEnd.getTime();
                    day.setTime(day.getTime() + (24 * 60 * 60 * 1000))) {
                printDayHotelSalesInfo(day, dayHotelSalesInfos[day])
            }
        }
    }
}

private void printShortcutReportHeader() {
    processNextCell(0, 0) {
        text("Дата", "header", 1, 3)
        columnWidth(15)
    }
    processNextCell(0, 1) {
        text("Продажа", "header", 4, 1)
    }
    processNextCell(0, 4) {
        text("Возврат", "header", 4, 1)
    }
    processNextCell(0, 4) {
        text("Количество субагентов", "header", 1, 3)
        columnWidth(10)
    }
    processNextCell(1, 1) {
        text("Общая", "header", 2, 1)
    }
    processNextCell(0, 2) {
        text("Вознаграждение субагента", "header", 1, 2)
        columnWidth(15)
    }
    processNextCell(0, 1) {
        text("Доход ВИП", "header", 1, 2)
        columnWidth(15)
    }
    processNextCell(0, 1) {
        text("Общая", "header", 2, 1)
    }
    processNextCell(0, 2) {
        text("Вознаграждение субагента", "header", 1, 2)
        columnWidth(15)
    }
    processNextCell(0, 1) {
        text("Доход ВИП", "header", 1, 2)
        columnWidth(15)
    }
    processNextCell(1, 1) {
        text("Заказов, шт.", "header")
        columnWidth(10)
    }
    processNextCell(0, 1) {
        text("Сумма, руб.")
        columnWidth(15)
    }
    processNextCell(0, 3) {
        text("Заказов, шт.", "header")
        columnWidth(10)
    }
    processNextCell(0, 1) {
        text("Сумма, руб.")
        columnWidth(15)
    }
}

private void printDayHotelSalesInfo(Date day, DayHotelSalesInfo info) {
    processNextCell(1, 0) {
        if (day != null) {
            text(dateFormat.format(day), "textData")
        } else {
            text("", "textData")
        }
    }
    processNextCell(0, 1) {
        number(info.saleBookingNumbers.size(), "numberData")
    }
    processNextCell(0, 1) {
        number(info.salePriceSum, "numberData")
    }
    processNextCell(0, 1) {
        number(info.saleSubagencyCommissionSum, "numberData")
    }
    processNextCell(0, 1) {
        number(info.saleVendorCommissionSum, "numberData")
    }
    processNextCell(0, 1) {
        number(info.refundBookingNumbers.size(), "numberData")
    }
    processNextCell(0, 1) {
        number(info.refundPriceSum, "numberData")
    }
    processNextCell(0, 1) {
        number(info.refundSubagencyCommissionSum, "numberData")
    }
    processNextCell(0, 1) {
        number(info.refundVendorCommissionSum, "numberData")
    }
    processNextCell(0, 1) {
        number(info.subagencyUids.size(), "numberData")
    }
}

private void fillSpreadSheetHeader() {
    processNextCell(0, 0) {
        rowHeight(30)
        text('Отчет по продажам отелей ', 'title')
    }
    processNextCell(1, 0) {
        rowHeight(13)
        text('Дата составления отчета:', 'metadataTitle')
    }
    processNextCell(0, 1) {
        date(new Date(), 'metadataDateValue')
    }
    processNextCell(1, 0) { text('Период:', 'metadataTitle') }
    processNextCell(0, 1) {
        text(parameters.REPORT_PERIOD, 'metadataValue')
    }
    if (!specifiedSubAgents.isEmpty()) {
        processNextCell(1, 0) { text('Субагент:', 'metadataTitle') }
        processNextCell(0, 1) { text(specifiedSubAgents, 'metadataValue') }
    }
    if (!specifiedAgents.isEmpty()) {
        processNextCell(1, 0) { text('Агент:', 'metadataTitle') }
        processNextCell(0, 1) { text(specifiedAgents, 'metadataValue') }
    }
    if (!specifiedProfileGroup.isEmpty()) {
        processNextCell(1, 0) { text('Группа:', 'metadataTitle') }
        processNextCell(0, 1) { text(specifiedProfileGroup, 'metadataValue') }
    }
    if (!specifiedGds.isEmpty()) {
        processNextCell(1, 0) { text('Система бронирования:', 'metadataTitle') }
        processNextCell(0, 1) { text(specifiedGds, 'metadataValue') }
    }
}

private void fillTableHeader() {
    int scw = 10
    processNextCell(2, 0) {
        setStyle('header')
        rowHeight(40)
        columnWidth(2 * scw)
        text('Агентство')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Наименование субагента')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Клиент')
    }
    if (isGroupedByProfileGroup || !specifiedProfileGroup.isEmpty()) {
        processNextCell(0, 1) {
            columnWidth(2 * scw)
            text('Группа')
        }
    }
    if (isGroupedByAgent || !specifiedAgents.isEmpty()) {
        processNextCell(0, 1) {
            columnWidth(2 * scw)
            text('Выписывающий агент')
        }
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Номер заказа поставщика')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Номер заказа Портбилет')
    }
    if(!isByDepartureDate){
        processNextCell(0, 1) {
            columnWidth(1.5 * scw);
            text('Холдированные средства');
        }
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Статус')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Дата оформления')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Дата начала оказания услуги')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Дата окончания оказания услуги (выезд)')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Страна оказания услуги')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Город')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Название отеля')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Имя путешественника')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Валюта взаиморасчетов')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Название поставщика')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Форма налогообложения')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Владелец бланков')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Стоимость поставщика')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Сбор ВИП-Сервис')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Общая стоимость проживания')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Штраф')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Агентское вознаграждение субагента')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Агентское вознаграждение ВИП-СЕРВИС')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('К оплате')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('НДС')
    }
    processNextCell(0, 1) {
        columnWidth(2 * scw)
        text('Источник')
    }
}

private void fillTable(ArrayList hotelList) {
    if ((isGroupedByAgent || !specifiedAgents.isEmpty()) &&
    (isGroupedByProfileGroup || !specifiedProfileGroup.isEmpty())) {
        hotelList = hotelList.sort { Hotel a, Hotel b ->
            a.subagentName <=> b.subagentName ?: a.group <=> b.group ?:
                    a.agent <=> b.agent ?:
                    a.productStatus <=> b.productStatus ?:
                    a.issueDate <=> b.issueDate
        }
    }
    else if (isGroupedByAgent || !specifiedAgents.isEmpty()) {
        hotelList = hotelList.sort { Hotel a, Hotel b ->
            a.subagentName <=> b.subagentName ?: a.agent <=> b.agent ?:
                    a.productStatus <=> b.productStatus ?:
                    a.issueDate <=> b.issueDate
        }
    }
    else if (isGroupedByProfileGroup || !specifiedProfileGroup.isEmpty()) {
        hotelList = hotelList.sort { Hotel a, Hotel b ->
            a.subagentName <=> b.subagentName ?: a.group <=> b.group ?:
                    a.productStatus <=> b.productStatus ?:
                    a.issueDate <=> b.issueDate
        }
    }
    else {
        hotelList = hotelList.sort { Hotel a, Hotel b ->
            a.subagentName <=> b.subagentName ?:
                    a.productStatus <=> b.productStatus ?:
                    a.issueDate <=> b.issueDate
        }
    }

    Hotel currentHotel
    Hotel nextHotel
    if (hotelList.size() > 0) {
        currentHotel = hotelList.first()
    }
    for (int i = 1; i <= hotelList.size(); i++) {
        if (i != hotelList.size()) {
            nextHotel = hotelList.get(i)
        }
        else {
            nextHotel = null
        }
        processNextCell(1, 0) {
            rowHeight(12)
            text(currentHotel?.agencyName, 'textData')
        }

        processNextCell(0, 1) {
            text(currentHotel?.subagentName, 'textData')
        }

        processNextCell(0, 1) {
            text(currentHotel?.clientName, 'textData')
        }

        if (isGroupedByProfileGroup || !specifiedProfileGroup.isEmpty()) {
            processNextCell(0, 1) {
                text(currentHotel?.group, 'textData')
            }
        }
        if (isGroupedByAgent || !specifiedAgents.isEmpty()) {
            processNextCell(0, 1) {
                text(currentHotel?.agent, 'textData')
            }
        }
        processNextCell(0, 1) {
            text(currentHotel?.reservationNumber, 'textData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.bookingNumberPortbilet, 'textData')
        }
        if(!isByDepartureDate){
            processNextCell(0, 1) {
                text(currentHotel.onHold, 'textData')
            }
        }
        processNextCell(0, 1) {
            String status = currentHotel?.productStatus?.toString()
            if (currentHotel.isRefund) {
                status += "*"
            }
            text(status, 'textData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.issueDate, 'textData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.checkIn, 'textData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.checkOut, 'textData')
        }
        if (isByDepartureDate) {
            processNextCell(0, 1) {
                text(currentHotel?.unholdDate, 'textData')
            }
        }
        processNextCell(0, 1) {
            text(currentHotel?.hotelCountry, 'textData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.hotelCity, 'textData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.hotelName, 'textData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.travellersName, 'textData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.currencyCode, 'textData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.supplier, 'textData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.formTax, 'textData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.blankOwner, 'textData')
        }
        processNextCell(0, 1) {
            number(currentHotel?.supplierFee, 'numberData')
        }
        processNextCell(0, 1) {
            number(currentHotel?.vipFee, 'numberData')
        }
        processNextCell(0, 1) {
            number(currentHotel?.totalFee, 'numberData')
        }
        processNextCell(0, 1) {
            number(currentHotel?.penalty, 'numberData')
        }
        processNextCell(0, 1) {
            number(currentHotel?.agencyCommision, 'numberData')
        }
        processNextCell(0, 1) {
            number(currentHotel?.vipCommision, 'numberData')
        }
        processNextCell(0, 1) {
            number(currentHotel?.toPaid, 'numberData')
        }
        processNextCell(0, 1) {
            number(currentHotel?.vat, 'numberData')
        }
        processNextCell(0, 1) {
            text(currentHotel?.actionSources, 'numberData')
        }

        fillTotalMap(currentHotel)

        boolean isAgentChanged = !currentHotel?.agent?.equals(nextHotel?.agent)
        boolean isGroupChanged = !currentHotel?.group?.equals(
                (nextHotel?.group))
        boolean isSubAgentChanged = !currentHotel?.subagentName?.equals(
                nextHotel?.subagentName)
        boolean isStatusChanged = !currentHotel?.productStatus?.equals(
                nextHotel?.productStatus)
        boolean needTotalForAgent = (isGroupedByAgent ||
                !specifiedAgents.isEmpty()) &&
                ((!isAgentChanged && isStatusChanged) || isAgentChanged)
        boolean needTotalForSubAgent = isSubAgentChanged
        boolean needTotalForGroup = (isGroupedByProfileGroup ||
                !specifiedProfileGroup.isEmpty()) && isGroupChanged

        if (isAgentChanged) {
            if (needTotalForAgent) {
                printAggregator(currentHotel.agent, agentAggregator)
            }
            agentAggregator = new AggregatorByStatus()
        }

        if (isGroupChanged) {
            if (needTotalForGroup) {
                printAggregator(currentHotel.group, groupAggregator)
            }
            groupAggregator = new AggregatorByStatus();
        }

        if (isSubAgentChanged) {
            if (needTotalForSubAgent) {
                printAggregator(currentHotel.subagentName, subagentAggregator)
            }
            subagentAggregator = new AggregatorByStatus();
        }
        currentHotel = nextHotel
        rowHeight(12)
    }
}

@Field AggregatorByStatus subagentAggregator = new AggregatorByStatus()
@Field AggregatorByStatus agentAggregator = new AggregatorByStatus()
@Field AggregatorByStatus groupAggregator = new AggregatorByStatus()
@Field AggregatorByStatus totalAggregator = new AggregatorByStatus()

private void fillTotalMap(Hotel hotel) {
    subagentAggregator.append(hotel)
    if (isGroupedByAgent || !specifiedAgents.isEmpty()) {
        agentAggregator.append(hotel)
    }
    if (isGroupedByProfileGroup || !specifiedProfileGroup.isEmpty()) {
        groupAggregator.append(hotel)
    }
    totalAggregator.append(hotel)
}

private void fillTableFooter() {
    Aggregator aggregatorSell = totalAggregator.byStatus.get(ProductStatus.SELL)
    Aggregator aggregatorRefund = totalAggregator.byStatus.get(ProductStatus.REFUND)
    Aggregator aggregatorBooking = totalAggregator.byStatus.get(ProductStatus.BOOKING)
    Aggregator aggregatorVoidBooking = totalAggregator.byStatus.get(ProductStatus.VOID_BOOKING)
    Aggregator aggregatorRequest = totalAggregator.byStatus.get(ProductStatus.REQUEST)
    Aggregator aggregatorReject = totalAggregator.byStatus.get(ProductStatus.REJECT)
    if (aggregatorSell) {
        printAggregator("${ProductStatus.SELL}:", aggregatorSell)
    }
    if (aggregatorRefund) {
        printAggregator("${ProductStatus.REFUND}:", aggregatorRefund)
    }
    if (aggregatorBooking) {
        printAggregator("${ProductStatus.BOOKING}:", aggregatorBooking)
    }
    if (aggregatorVoidBooking) {
        printAggregator("${ProductStatus.VOID_BOOKING}:", aggregatorVoidBooking)
    }
    if (aggregatorRequest) {
        printAggregator("${ProductStatus.REQUEST}:", aggregatorRequest)
    }
    if (aggregatorReject) {
        printAggregator("${ProductStatus.REJECT}:", aggregatorReject)
    }
    printAggregator('ИТОГО:', totalAggregator.total)
}

private int getColumnShiftTotal(int baseShift) {
    if ((isGroupedByProfileGroup || !specifiedProfileGroup.isEmpty())) {
        baseShift++
    }
    if (isGroupedByAgent || !specifiedAgents.isEmpty()) {
        baseShift++
    }
    return baseShift
}

private void processNextCell(int numberOfRowShifts, int numberOfColumnShifts,
        Closure action) {
    numberOfRowShifts.times { nextRow() }
    numberOfColumnShifts.times { nextColumn() }
    action()
}

private void populateBlankOwner(HotelProduct hotelProduct, Hotel hotel) {
    String blankOwner = 'отсутствует'
    if (hotelProduct != null) {
        EntityReference<Organization> ownerRef = hotelProduct.getBlankOwnerRef()
        if (ownerRef != null) {
            if (ownerRef.getCaption() != null) {
                blankOwner = ownerRef.getCaption()
            } else {
                blankOwner = EntityStorage.get().resolve(ownerRef).toString()
            }
        } else {
            String code = hotelProduct.getBlankOwnerCode()
            if (code != null) {
                blankOwner = code;
            }
        }
    }

    hotel.setBlankOwner(blankOwner)
}

private void populateTaxForm(EntityReference<Organization> subagencyRef,
        Hotel hotel) {
    if (subagencyRef != null) {
        Organization organization = EntityStorage.get().resolve(
                subagencyRef)?.getEntity()
        if (organization == null) {
            return;
        }
        if (organization.simpleTaxed) {
            hotel.setFormTax("УСН")
        }
        else {
            hotel.setFormTax("ОСН")
        }
    }
}

private void populateAgency(Hotel hotel, EntityReference<Organization> agencyRef) {
    if (agencyRef != null) {
        hotel.setAgencyName(agencyRef.toString())
    }
}

private void populateSubAgent(Hotel hotel,
        EntityReference<Organization> subagencyRef) {
    hotel.setSubagentName(subagencyRef.toString())
}

private void populateClient(Hotel hotel, ProductInfo productInfo) {
    hotel.setClientName(productInfo.clientName)
}

private void populateAgent(Reservation reservation, Hotel hotel) {
    EntityReference<Person> bookingAgent = reservation.getBookingAgent()
    if (bookingAgent != null) {
        EntityContainer<Person> bookingAgentCont =
                EntityStorage.get().resolve(bookingAgent)
        if (bookingAgentCont != null) {
            Person person = bookingAgentCont.getEntity()
            Locale locale = person.getPreferredLocale()
            String fullName = ""
            String lastName = L10nStringHelper.getValue(person.getLastName(),
                    locale, false)

            String firstName =
                    L10nStringHelper.getValue(person.getFirstName(), locale,
                    false)

            if (lastName != null) {
                fullName = fullName + lastName
            }
            if (firstName != null) {
                fullName = fullName + firstName
            }
            if (!fullName.isEmpty()) {
                hotel.setAgent(fullName)
            }
        }
    }
}

@Field Set vendorContractTypes = [ContractType.VENDOR] as Set
@Field Set commissionPropertyTypes = [CommissionProperties.class, DiscountProperties.class] as Set
@Field Set subagencyContractTypes = [ContractType.SUBAGENCY] as Set

Collection filterCommissionsByContractTypes(
            final Collection<GeneralProductCommission> commissions,
            final Set<ContractType> contractTypes) {

        if (commissions == null) {
            return Collections.emptyList();
        }

        if (contractTypes == null) {
            return new ArrayList<GeneralProductCommission>(commissions);
        }

        Collection<GeneralProductCommission> filteredCommissions =
            new ArrayList<GeneralProductCommission>();

        for (GeneralProductCommission commission : commissions) {

            if (contractTypes.contains(commission.getContractType())) {
                filteredCommissions.add(commission);
            }
        }

        return filteredCommissions;
    }

private void populateVendorCommission(HotelProduct hotelProduct, boolean negate,
        Hotel hotel) {
    Collection commissions = HotelProductHelper.getUnmodifiableCommissions(hotelProduct, ContractType.VENDOR)
    commissions = filterCommissionsByContractTypes(commissions, vendorContractTypes)
    commissions = GeneralProductHelper.filterCommissionsByCommissionProperties(commissions, commissionPropertyTypes)
    BigDecimal vendorCommission = GeneralProductHelper.calculateCommissionsEquivalentValue(commissions)
    if (vendorCommission == null) {
        vendorCommission = BigDecimal.ZERO
    }
    if (negate) {
        vendorCommission = vendorCommission.negate()
    }
    hotel.setVipCommision(
            vendorCommission == null ? BigDecimal.ZERO : vendorCommission)
}

private void populateSubAgencyComission(HotelProduct hotelProduct,
        boolean negate, Hotel hotel) {
    Collection commissions = HotelProductHelper.getUnmodifiableCommissions(hotelProduct, ContractType.SUBAGENCY)
    commissions = filterCommissionsByContractTypes(commissions, subagencyContractTypes)
    commissions = GeneralProductHelper.filterCommissionsByCommissionProperties(commissions, commissionPropertyTypes)
    BigDecimal subagencyCommission = GeneralProductHelper.calculateCommissionsEquivalentValue(commissions)
    if (subagencyCommission == null) {
        subagencyCommission = BigDecimal.ZERO
    }
    if (negate) {
        subagencyCommission = subagencyCommission.negate()
    }
    hotel.setAgencyCommision(subagencyCommission)
}

private void populateSupplierFee(HotelProduct hotelProduct, boolean negate,
        Hotel hotel) {
    BigDecimal totalFare = HotelProductHelper.calculateTotalEquivalentFare(
            hotelProduct)

    if (totalFare == null) {
        totalFare = BigDecimal.ZERO
    }
    if (negate) {
        totalFare = totalFare.negate()
    }
    hotel.setSupplierFee(totalFare)
}

private void populatePrtbBookingNumber(BookingFile bookingFile, Hotel hotel) {
    String bookingNumber = bookingFile.getNumber()
    hotel.setBookingNumberPortbilet(bookingNumber)
}

private void populateReservationNumber(Reservation reservation, Hotel hotel) {
    String recordLocator = CommonReservationGdsNameInfoHelper
            .getDisplayedRecordLocator(reservation)
    hotel.setReservationNumber(recordLocator)
}

private void populateCurrencyCode(Hotel hotel) {
    hotel.setCurrencyCode(DictHelper.getLocalCurrencyCode())
}

private void populateTravellersNames(HotelProduct hotelProduct, Hotel hotel) {
    List<Traveller> travellerList = hotelProduct.getTravellers()
    if (travellerList != null && !travellerList.isEmpty()) {
        Traveller traveller = travellerList.get(0)
        String name = traveller.getName()
        hotel.setTravellersName(name)
    }
}

private void populateHotelName(Hotel hotel, HotelProduct hotelProduct) {
    hotel.setHotelName(hotelProduct.getHotelName())
}

private void populateHotelCity(HotelProduct hotelProduct, Hotel hotel) {
    if (hotelProduct.getHotelLocation() != null) {
        GeoLocation location = DictHelper.findCity(
                hotelProduct.getHotelLocation())
        if (location != null) {
            hotel.setHotelCity(location.toString())
        }
    }
}

private void populateHotelCountry(HotelProduct hotelProduct, Hotel hotel) {
    if (hotelProduct.getHotelLocation() != null) {
        GeoLocation result =
                Environment.getPublished(
                DictionaryCache.class).resolveReference(
                hotelProduct.getHotelLocation())
        if (result != null && result.getCountry() != null) {
            hotel.setHotelCountry(result.getCountry().toString())
        }
    }
}

private void populateProductStatus(HotelProduct hotelProduct, Hotel hotel) {
    ProductStatus status = hotelProduct.getStatus()
    hotel.setProductStatus(status)
}

private void populateIssueDate(HotelProduct hotelProduct, Hotel hotel) {
    if (hotelProduct.getIssueDate() instanceof java.util.Date) {
        String issueDate = dateFormat.format(hotelProduct.getIssueDate())
        hotel.setIssueDate(issueDate)
    } else {
        hotel.setIssueDate("")
    }
}

private void populateCheckInDate(HotelProduct hotelProduct, Hotel hotel) {
    Date checkInDate
    if (hotelProduct.rooms.size() == 1) {
        checkInDate = hotelProduct.rooms.get(0).getCheckInDate()
    } else {
        checkInDate = HotelProductHelper.getFirstCheckinDate(hotelProduct)
    }
    if (checkInDate != null) {
        String checkIn = dateFormat.format(checkInDate)
        hotel.setCheckIn(checkIn)
    }
}

private void populateUnholdDate(HotelProduct hotelProduct, Hotel hotel) {
    Date unholdDate = hotelProduct.getUnholdDate()
    String unhold
    if (hotelProduct.status == ProductStatus.REFUND) {
        unhold =  dateFormat.format(unholdDate)
    } else {
        unhold = ''
    }
    hotel.setUnholdDate(unhold)
}

private void populateCheckOutDate(HotelProduct hotelProduct, Hotel hotel) {
    Date checkOutDate
    if (hotelProduct.rooms.size() == 1) {
        checkOutDate = hotelProduct.rooms.get(0).getCheckOutDate()
    } else {
        checkOutDate = HotelProductHelper.getLastCheckoutDate(hotelProduct)
    }
    if (checkOutDate != null) {
        String checkOut = dateFormat.format(checkOutDate)
        hotel.setCheckOut(checkOut)
    }
}

private void populateSupplier(HotelProduct product, Hotel hotel) {
    String supplierName = 'отсутствует';
    EntityReference<Organization> supplier = HotelProductHelper.getSupplier(product);

    if (supplier != null) {
        supplierName = supplier?.caption.toString() ?: 'отсутствует';
    }
    hotel.setSupplier(supplierName);
}

private void populateProfileGroup(String profileGroup, Hotel ticket) {
    ticket.setGroup(profileGroup)
}

private void populateTotalFee(HotelProduct product, Hotel ticket) {
    BigDecimal totalFee = BigDecimal.ZERO  

        List<HotelProductFop> fops = HotelProductHelper.getSubagentFops(product, false)
        for (HotelProductFop fop : fops) {
            totalFee += getFopFee(fop, product.issueDate)
        }

    if ((product.getStatus() == ProductStatus.REFUND) || (product.getStatus() == ProductStatus.EXCHANGE)) {
        /*totalFee = totalFee == null ? totalFee : totalFee.negate();*/
        totalFee = totalFee.negate();
    }
    ticket.setTotalFee(totalFee);
}

BigDecimal getFopFee(HotelProductFop fop, Date issueDate) {
    if (fop.amount?.value == null) {
        return BigDecimal.ZERO
    }
    String eqCurrCode = DictHelper.getLocalCurrencyCode()
    if (fop.amount.currency == null || fop.amount.currency.currencyCode == eqCurrCode) {
        return fop.amount.value
    }
    CurrencyRateType rateType = FinanceHelper.getCurrencyRateType(eqCurrCode)
    CurrencyRate currencyRate = DictHelper.findCurrencyRate(fop.amount.currency.currencyCode, issueDate, rateType, null)
    if (currencyRate == null) {
        return null
    }
    return fop.amount.value * BigDecimal.valueOf(currencyRate.rate)
}

private void populateToPaid(HotelProduct product, Hotel ticket) {
    BigDecimal sf = ticket?.supplierFee     ?: BigDecimal.ZERO;
    BigDecimal vc = ticket?.vipCommision    ?: BigDecimal.ZERO;
    BigDecimal totalFee = sf.subtract(vc);

    ticket.setToPaid(totalFee);
}

private void populateOnHold(HotelProduct product, Hotel ticket) {
    ticket.setOnHold(product.onHold? '+' : '-');
}

private void populateVat(HotelProduct product, Hotel ticket) {
    ticket.setVat(MiscUtil.sum(HotelProductHelper.calculateTotalVatAmount(product), ticket.getVat()));
}

warn 'reportVersion=' + '0.0.5'
