From c0d092a7b4cc31e3995a09f2c142c684b930aad4 Mon Sep 17 00:00:00 2001 From: "MSI\\derek" Date: Wed, 25 Sep 2024 10:45:04 +0800 Subject: [PATCH] update dashboard & report --- .../data/entity/GradeLogRepository.java | 4 + .../modules/data/service/DashboardService.kt | 14 +- .../modules/report/service/ReportService.kt | 401 +++++++++++++++++- .../modules/report/web/ReportController.kt | 6 +- .../report/Cross Team Charge Report.xlsx | Bin 18321 -> 18315 bytes 5 files changed, 417 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/ffii/tsms/modules/data/entity/GradeLogRepository.java b/src/main/java/com/ffii/tsms/modules/data/entity/GradeLogRepository.java index 5daad9b..4ae08ac 100644 --- a/src/main/java/com/ffii/tsms/modules/data/entity/GradeLogRepository.java +++ b/src/main/java/com/ffii/tsms/modules/data/entity/GradeLogRepository.java @@ -3,9 +3,13 @@ package com.ffii.tsms.modules.data.entity; import com.ffii.core.support.AbstractRepository; import com.ffii.tsms.modules.data.entity.projections.GradeLogInfo; +import java.time.LocalDate; import java.util.List; public interface GradeLogRepository extends AbstractRepository { List findGradeLogInfoByStaffIdAndDeletedFalseOrderByCreatedDesc(Long staffId); GradeLog findFirstByStaffIdAndDeletedFalseOrderByCreatedDesc(Long staffId); + + List findByDeletedFalseAndFromBeforeAndToIsNullOrToAfter(LocalDate before, LocalDate after); +// fun findByDeletedFalseAndRecordDateBetweenOrderByRecordDate(start: LocalDate, end: LocalDate): List } diff --git a/src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt b/src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt index 2e7dc64..b429a19 100644 --- a/src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt +++ b/src/main/java/com/ffii/tsms/modules/data/service/DashboardService.kt @@ -2217,11 +2217,19 @@ open class DashboardService( // + " ) as expenditure on 1 = 1" // + " where p.id = :projectId" // + " group by expenditure.expenditure" - " select" + "with pe_cte as (" + + " select " + + " pe.projectId " + + " ,sum(pe.amount) as expense " + + " from project_expense pe " + + " where pe.deleted = false " + + " group by pe.projectId " + + " ) " + + " select" + " concat(p.code,'-',p.name) as projectCodeAndName," + " p.expectedTotalFee as totalFee," + " (p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8 as totalBudget," - + " coalesce (expenditure.expenditure,0) as expenditure," + + " coalesce (expenditure.expenditure,0) + coalesce(pc.expense, 0) as expenditure, " + " ((p.expectedTotalFee - ifnull(p.subContractFee, 0)) * 0.8) - coalesce (expenditure.expenditure,0) as remainingBudget," + " case" + " when p.expectedTotalFee - coalesce (expenditure.expenditure,0) >= 0 then 'Within Budget'" @@ -2230,7 +2238,9 @@ open class DashboardService( + " p.totalManhour as plannedResources," + " sum(ifnull(t.normalConsumed, 0) + ifnull(t.otConsumed, 0)) as resourcesSpent," + " p.totalManhour - sum(ifnull(t.normalConsumed, 0) + ifnull(t.otConsumed, 0)) as remainingResources" + + " , pc.expense" + " from project p" + + " left join pe_cte pc on pc.projectId = p.id " + " left join project_task pt on p.id = pt.project_id" + " left join timesheet t on pt.id = t.projectTaskId" + " left join(" diff --git a/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt b/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt index 650c151..112b689 100644 --- a/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt +++ b/src/main/java/com/ffii/tsms/modules/report/service/ReportService.kt @@ -33,17 +33,23 @@ import java.util.* import java.awt.Color import java.math.RoundingMode import java.time.Year +import javax.swing.plaf.synth.Region import kotlin.collections.ArrayList data class DayInfo(val date: String?, val weekday: String?) - +data class TsData(var manhour: Double, var cost: Double) +data class InOut(var In: TsData, var Out: TsData) @Service open class ReportService( private val jdbcDao: JdbcDao, private val projectRepository: ProjectRepository, - private val salaryEffectiveService: SalaryEffectiveService, private val salaryRepository: SalaryRepository, private val timesheetRepository: TimesheetRepository + private val salaryEffectiveService: SalaryEffectiveService, + private val salaryEffectiveRepository: SalaryEffectiveRepository, + private val salaryRepository: SalaryRepository, + private val timesheetRepository: TimesheetRepository, + private val teamLogRepository: TeamLogRepository ) { private val logger: Log = LogFactory.getLog(javaClass) private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy/MM/dd") @@ -63,6 +69,45 @@ open class ReportService( private val COMPLETION_PROJECT = "templates/report/AR05_Project Completion Report.xlsx" private val CROSS_TEAM_CHARGE_REPORT = "templates/report/Cross Team Charge Report.xlsx" + private fun cellBorderArgs(top: Int, bottom: Int, left: Int, right: Int): MutableMap { + var cellBorderArgs = mutableMapOf() + when (top) { + 1 -> cellBorderArgs.put(CellUtil.BORDER_TOP, BorderStyle.THIN) + 2 -> cellBorderArgs.put(CellUtil.BORDER_TOP, BorderStyle.THICK) + } + when (bottom) { + 1 -> cellBorderArgs.put(CellUtil.BORDER_BOTTOM, BorderStyle.THIN) + 2 -> cellBorderArgs.put(CellUtil.BORDER_BOTTOM, BorderStyle.THICK) + } + when (left) { + 1 -> cellBorderArgs.put(CellUtil.BORDER_LEFT, BorderStyle.THIN) + 2 -> cellBorderArgs.put(CellUtil.BORDER_LEFT, BorderStyle.THICK) + } + when (right) { + 1 -> cellBorderArgs.put(CellUtil.BORDER_RIGHT, BorderStyle.THIN) + 2 -> cellBorderArgs.put(CellUtil.BORDER_RIGHT, BorderStyle.THICK) + } + return cellBorderArgs + } + private fun setRegionBorders(top: Int, bottom: Int, left: Int, right: Int, region: CellRangeAddress, sheet: Sheet) { + when (top) { + 1 -> RegionUtil.setBorderTop(BorderStyle.THIN, region, sheet) + 2 -> RegionUtil.setBorderTop(BorderStyle.THICK, region, sheet) + } + when (bottom) { + 1 -> RegionUtil.setBorderBottom(BorderStyle.THIN, region, sheet) + 2 -> RegionUtil.setBorderBottom(BorderStyle.THICK, region, sheet) + } + when (left) { + 1 -> RegionUtil.setBorderLeft(BorderStyle.THIN, region, sheet) + 2 -> RegionUtil.setBorderLeft(BorderStyle.THICK, region, sheet) + } + when (right) { + 1 -> RegionUtil.setBorderRight(BorderStyle.THIN, region, sheet) + 2 -> RegionUtil.setBorderRight(BorderStyle.THICK, region, sheet) + } + } + private val chargeFee = 1.15 private fun conditionalFormattingNegative(sheet: Sheet) { // Create a conditional formatting rule @@ -330,6 +375,7 @@ open class ReportService( grades: List, monthlyStaffSalaryEffective: List, teamId: String, + gradeLog: List, ): ByteArray { // Generate the Excel report with query results val workbook: Workbook = @@ -340,6 +386,7 @@ open class ReportService( grades, monthlyStaffSalaryEffective, teamId, + gradeLog, CROSS_TEAM_CHARGE_REPORT ) @@ -3547,6 +3594,342 @@ open class ReportService( return jdbcDao.queryForList(sql.toString(), args) } + private fun generateTeamBlock( + teams: List, + sortedTeams: MutableList, + grades: List, + timesheets: List, + gradeLog: List, + salaryEffective: List, + ): MutableMap> { +// val teamlog = teamLogRepository.findAll().filter { it.deleted == false } + val gradeMap: MutableMap = mutableMapOf() + var teamsMap: MutableMap> = mutableMapOf() + grades.forEach { + val key = it.code + if (key !in gradeMap) { + gradeMap[key] = InOut( + TsData(0.0, 0.0), + TsData(0.0, 0.0) + ) + } + } + teams.forEach { team -> + val key = team.code + if (key !in teamsMap) { + // Create a new map for each team with copies of the entries from gradeMap + teamsMap[key] = gradeMap.mapValues { (_, value) -> + InOut( + TsData(value.In.manhour, value.In.cost), + TsData(value.Out.manhour, value.Out.cost) + ) + }.toMutableMap() + } + } + println("teamsMap") + println(gradeMap) +// teamsMap["TW"]!!["1"]!!.In.manhour += 1000 + println(teamsMap) + sortedTeams.forEach { team -> + val currentTeam = team.code + val _timesheets = timesheets.filter { ts -> +// for team log +// val thisTeam = teamlog.filter { +// it.from.isBefore(ts.recordDate) +// && it.to != null +// && it.staff.id == ts.staff!!.id +// }.maxByOrNull { it.from } + ts.project!!.teamLead!!.team.code == currentTeam // check isChargingTeam + && + ts.project!!.teamLead!!.team.code != ts.staff!!.team.code // check isCrossTeam +// ts.project!!.teamLead!!.team.id != thisTeam!!.team.id // for team log + } + _timesheets.forEach {ts -> + // this team charging others + // get the grade and salary data of the record + val _grade = gradeLog.find { it.staff.id == ts.staff!!.id } + val gradeCode = _grade!!.grade.code + val otMultiplier = 1 + val thisSE = salaryEffective.filter { + it.staff.id == ts.staff!!.id + && + it.date.isBefore(ts.recordDate) + }.maxByOrNull { it.date } + val normalHour = ts.normalConsumed ?: 0.0 + val otHour = ts.otConsumed ?: 0.0 + val normalCost = normalHour.times(thisSE!!.salary.hourlyRate.toDouble()) + val otCost = otHour.times(thisSE.salary.hourlyRate.toDouble()).times(otMultiplier) + //assigning data + val projectTeam = ts.project!!.teamLead!!.team.code + val staffTeam = ts.staff!!.team.code + // write in + println("putting in") + var tsInData = teamsMap[projectTeam]!![gradeCode]!!.In + println(tsInData) + tsInData.manhour += normalHour + otHour + tsInData.cost += normalCost + otCost + // write out + println("putting out") + val tsOutData = teamsMap[staffTeam]!!.get(gradeCode)!!.Out + tsOutData.manhour += normalHour + otHour + tsOutData.cost += normalCost + otCost + } + } + println("all team - gradeMap") + println(teamsMap) + return teamsMap + } + + + private fun alignTopCenter(tempCell: Cell) { + CellUtil.setVerticalAlignment(tempCell, VerticalAlignment.TOP) + CellUtil.setAlignment(tempCell, HorizontalAlignment.CENTER) + } + private fun createCrossTeamForm( + workbook: Workbook, + sheet: Sheet, + _rowIndex: Int, + _columnIndex: Int, + teamsMap: MutableMap> + ) { + var rowIndex = _rowIndex + var columnIndex = _columnIndex + var tempRow: Row + var tempCell: Cell + + val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)") + + fun dataFormatArgs(accountingStyle: Short): MutableMap { + val dataFormatArgs = mutableMapOf( + CellUtil.DATA_FORMAT to accountingStyle + ) + return dataFormatArgs + } + + fun fontArgs(isBold: Boolean): MutableMap{ + val font = sheet.workbook.createFont() + font.bold = isBold + font.fontName = "Times New Roman" + val fontArgs = mutableMapOf( + CellUtil.FONT to font.index, + CellUtil.WRAP_TEXT to true, + ) + return fontArgs + } + + val InHourLetter = CellReference.convertNumToColString(3) // col D + val InCostLetter = CellReference.convertNumToColString(4) // col E + val OutHourLetter = CellReference.convertNumToColString(5) // col F + val OutCostLetter = CellReference.convertNumToColString(6) // col G + val NetHourLetter = CellReference.convertNumToColString(7) // col G + val NetCostLetter = CellReference.convertNumToColString(8) // col G + val subTotalRowIndexList = mutableListOf() + + teamsMap.forEach{ (teamCode, gradeMap) -> + columnIndex = 0 + tempRow = getOrCreateRow(sheet, rowIndex) + tempCell = getOrCreateCell(tempRow, columnIndex) + tempCell.setCellValue(teamCode) + alignTopCenter(tempCell) + val teamCodeRegion = CellRangeAddress(rowIndex, rowIndex + 5,0,0) + sheet.addMergedRegion(teamCodeRegion) + setRegionBorders(2,2,1,1, teamCodeRegion, sheet) + CellUtil.setCellStyleProperties(tempCell, fontArgs(false)) + columnIndex++ + + val gradeRegion = CellRangeAddress(rowIndex, rowIndex + 4,1,1) + tempCell = getOrCreateCell(tempRow, columnIndex) + tempCell.setCellValue("Grade") + setRegionBorders(1,1,1,1, gradeRegion, sheet) + CellUtil.setCellStyleProperties(tempCell, fontArgs(true)) + alignTopCenter(tempCell) + sheet.addMergedRegion(gradeRegion) + columnIndex++ + + gradeMap.forEach{ (gradeCode, inOut) -> + tempRow = getOrCreateRow(sheet, rowIndex) + tempCell = getOrCreateCell(tempRow, columnIndex).apply { + setCellValue(gradeCode) + alignTopCenter(this) + } + CellUtil.setCellStyleProperties(tempCell, cellBorderArgs(1,1,1,1)+fontArgs(false)) + for (i in 1..6) { // horizontal loop: payment In & payment Out & Net Amount + tempCell = getOrCreateCell(tempRow, columnIndex + i).apply { + when (i) { + 1 -> setCellValue(inOut.In.manhour) + 2 -> setCellValue(inOut.In.cost) + 3 -> setCellValue(inOut.Out.manhour) + 4 -> setCellValue(inOut.Out.cost) + 5 -> cellFormula = "${InHourLetter}${rowIndex+1}-${OutHourLetter}${rowIndex+1}" + 6 -> cellFormula = "${InCostLetter}${rowIndex+1}-${OutCostLetter}${rowIndex+1}" + } + CellUtil.setCellStyleProperties(this, + cellBorderArgs(1,1,1,1) + + fontArgs(false) + + dataFormatArgs(accountingStyle) + ) + } + } // end loop: payment In & payment Out & Net Amount + rowIndex++ + } + // write subtotal + columnIndex = 1 + tempRow = getOrCreateRow(sheet, rowIndex) + tempCell = getOrCreateCell(tempRow, columnIndex).apply { + setCellValue("Sub-total:") + CellUtil.setCellStyleProperties(this, + cellBorderArgs(1,1,1,1) + + fontArgs(false)) + alignTopCenter(this) + } + subTotalRowIndexList.add(rowIndex) + // InHour subtotal + columnIndex = 3 + tempCell = getOrCreateCell(tempRow, columnIndex).apply { + cellFormula = "SUM(${InHourLetter}${rowIndex-4}:${InHourLetter}${rowIndex})" + CellUtil.setCellStyleProperties(this, + cellBorderArgs(1,1,1,1) + + fontArgs(false) + + dataFormatArgs(accountingStyle) + ) + } + // InCost subtotal + columnIndex = 4 + tempCell = getOrCreateCell(tempRow, columnIndex).apply { + cellFormula = "SUM(${InCostLetter}${rowIndex-4}:${InCostLetter}${rowIndex})" + CellUtil.setCellStyleProperties(this, + cellBorderArgs(1,1,1,1) + + fontArgs(false) + + dataFormatArgs(accountingStyle) + ) + } + // OutHour subtotal + columnIndex = 5 + tempCell = getOrCreateCell(tempRow, columnIndex).apply { + cellFormula = "SUM(${OutHourLetter}${rowIndex-4}:${OutHourLetter}${rowIndex})" + CellUtil.setCellStyleProperties(this, + cellBorderArgs(1,1,1,1) + + fontArgs(false) + + dataFormatArgs(accountingStyle) + ) + } + // OutCost subtotal + columnIndex = 6 + tempCell = getOrCreateCell(tempRow, columnIndex).apply { + cellFormula = "SUM(${OutCostLetter}${rowIndex-4}:${OutCostLetter}${rowIndex})" + CellUtil.setCellStyleProperties(this, + cellBorderArgs(1,1,1,1) + + fontArgs(false) + + dataFormatArgs(accountingStyle) + ) + } + // NetHour subtotal + columnIndex = 7 + tempCell = getOrCreateCell(tempRow, columnIndex).apply { + cellFormula = "SUM(${NetHourLetter}${rowIndex-4}:${NetHourLetter}${rowIndex})" + CellUtil.setCellStyleProperties(this, cellBorderArgs(1,1,1,1) + + fontArgs(false) + + dataFormatArgs(accountingStyle) + ) + } + // NetCost subtotal + columnIndex = 8 + tempCell = getOrCreateCell(tempRow, columnIndex).apply { + cellFormula = "SUM(${NetCostLetter}${rowIndex-4}:${NetCostLetter}${rowIndex})" + CellUtil.setCellStyleProperties(this, cellBorderArgs(1,1,1,1) + + fontArgs(false) + + dataFormatArgs(accountingStyle) + ) + } + // merge at last + val subtotalRegion = CellRangeAddress(rowIndex, rowIndex,1,2) + sheet.addMergedRegion(subtotalRegion) + val subtotalRowRegion = CellRangeAddress(rowIndex, rowIndex,0,8) + setRegionBorders(1,2,1,1, subtotalRowRegion, sheet) + rowIndex++ + } + // rowIndex == 47 + columnIndex = 1 + tempRow = getOrCreateRow(sheet, rowIndex) + tempCell = getOrCreateCell(tempRow, columnIndex).apply { + setCellValue("Total:") + CellUtil.setCellStyleProperties(this, + cellBorderArgs(1,1,1,1) + + fontArgs(false) + ) + alignTopCenter(this) + } + val totalRegion = CellRangeAddress(rowIndex, rowIndex,1,2) + sheet.addMergedRegion(totalRegion) + for (i in 3..8) { + columnIndex = i + val subtotalList = mutableListOf() + val currColumnLetter = CellReference.convertNumToColString(i) + subTotalRowIndexList.forEach{ + subtotalList.add("${currColumnLetter}${it+1}") + } + val subtotalFormula = subtotalList.joinToString("+") + tempCell = getOrCreateCell(tempRow, columnIndex).apply { + cellFormula = subtotalFormula + CellUtil.setCellStyleProperties(this, cellBorderArgs(1,1,1,1) + + fontArgs(false) + + dataFormatArgs(accountingStyle) + ) + } + } + val subtotalRowRegion = CellRangeAddress(rowIndex, rowIndex,0,8) + setRegionBorders(1,2,1,1, subtotalRowRegion, sheet) + } + + private fun createThirdSheetTeamChargeReport( + month: String, + workbook: Workbook, + timesheets: List, + teams: List, + grades: List, + teamId: String, + gradeLog: List, + isTeamLead: Boolean, + ) { + val salaryEffective = salaryEffectiveRepository.findAll() + var sheet: Sheet = workbook.getSheetAt(2) + val accountingStyle = workbook.createDataFormat().getFormat("_(* #,##0.00_);_(* (#,##0.00);_(* \"-\"??_);_(@_)") + val rowIndex = 1 // Assuming the location is in (1,2), which is the report date field + val columnIndex = 2 + val monthFormat = DateTimeFormatter.ofPattern("MMMM yyyy", Locale.ENGLISH) + val reportMonth = YearMonth.parse(month, DateTimeFormatter.ofPattern("yyyy-MM")) + val convertReportMonth = YearMonth.of(reportMonth.year, reportMonth.month).format(monthFormat) + sheet.getRow(rowIndex).getCell(columnIndex).apply { + setCellValue(convertReportMonth) + } + var sortedTeams = teams.sortedBy { it.id }.toMutableList() + if (teamId.lowercase() != "all") { +// sortedTeams = teams.sortedWith(compareBy { if (it.id == teamId.toLong()) 0 else 1 }).toMutableList() + sortedTeams = mutableListOf(sortedTeams.find { teamId.toLong() == it.id }!!) + } + //// generate info map ///// + val teamsInOutMap: MutableMap> = generateTeamBlock( + teams, + sortedTeams, + grades, + timesheets, + gradeLog, + salaryEffective, + ) + ///// end of function //// + ///// create the form //// + createCrossTeamForm( + workbook, + sheet, + 5, + 0, + teamsInOutMap + ) + // autosize + for (i in 3..8) { + sheet.setColumnWidth(i, 20 * 256) + } + } @Throws(IOException::class) private fun createCrossTeamChargeReport( month: String, @@ -3555,6 +3938,7 @@ open class ReportService( grades: List, monthlyStaffSalaryEffective: List, teamId: String, + gradeLog: List, templatePath: String, ): Workbook { // please create a new function for each report template @@ -4119,10 +4503,19 @@ open class ReportService( } } } - + val isTeamLead = false conditionalFormattingNegative(sheet) conditionalFormattingPositive(sheet) - + createThirdSheetTeamChargeReport( + month, + workbook, + timesheets, + teams, + grades, + teamId, + gradeLog, + isTeamLead, + ) return workbook } // Use to Calculate cummunlative expenditure diff --git a/src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt b/src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt index 62d0190..9d5ccc2 100644 --- a/src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt +++ b/src/main/java/com/ffii/tsms/modules/report/web/ReportController.kt @@ -63,6 +63,7 @@ class ReportController( private val invoiceService: InvoiceService, private val gradeAllocationRepository: GradeAllocationRepository, private val subsidiaryRepository: SubsidiaryRepository, private val staffAllocationRepository: StaffAllocationRepository, private val gradeRepository: GradeRepository, + private val gradeLogRepository: GradeLogRepository, private val salaryEffectiveService: SalaryEffectiveService, private val projectExpenseRepository: ProjectExpenseRepository, ) { @@ -342,8 +343,9 @@ class ReportController( val teams = teamRepository.findAll().filter { it.deleted == false } val grades = gradeRepository.findAll().filter { it.deleted == false } val monthlyStaffSalaryEffective = salaryEffectiveService.getMonthlyStaffSalaryData(startDate, endDate) - - val reportResult: ByteArray = excelReportService.generateCrossTeamChargeReport(request.month, timesheets, teams, grades, monthlyStaffSalaryEffective, request.teamId) + val gradeLog = gradeLogRepository.findByDeletedFalseAndFromBeforeAndToIsNullOrToAfter(startDate, endDate) + print(gradeLog) + val reportResult: ByteArray = excelReportService.generateCrossTeamChargeReport(request.month, timesheets, teams, grades, monthlyStaffSalaryEffective, request.teamId, gradeLog) return ResponseEntity.ok() .header("filename", "Cross Team Charge Report - " + LocalDate.now() + ".xlsx") .body(ByteArrayResource(reportResult)) diff --git a/src/main/resources/templates/report/Cross Team Charge Report.xlsx b/src/main/resources/templates/report/Cross Team Charge Report.xlsx index 02a17c81c7c7632e85135887a0e8038ceea65b4f..cc17ad61ae1a416f46cf09159f011e75d511762c 100644 GIT binary patch delta 7869 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

-tiasvCGaFmrm)E@fJxV5 zx*b~2{q(=8Y>1Aq!yn@chwhY7p!=Q(x=cN%lT;YD`?QG9z?W^sG_!w?kku}^(L(`2|VvGWX`w=<`7pq3QG3%2kn#v6)eJ1f2~yUzRgwY$!nPE9tHMB1 zM=!|{@i<=Dp3Z3`TyGKD>kfOu_L44gMWS5qxqxVat0i-(?uUm=f^7#h6&Q@Hca5u> z3G?sLHS6hP*cvg?AF@A4Kcs^mIPwY!Ax^22GQ_j_f`OPU|`t*|4+peaxJX| zQw>p)p|?he!I4S`Pq_{0;oqc^c9o!$$Uxat#0w{U19% z280!Z1i~kWP5K|<2K~Q5W)L-57SexqnE!1rgFs|iAUn8RrK{_oR9jO8;bqMKAh7`kFUl6V%%9%>0FWGsRR910 delta 7764 zcmb_>WmFtnv-aQ&I=H(N+zAeWAi>?;-3c~00fG(|G-zyX>>;AaCdUfyW{p{V<)lXMf)lNu8NKHl{(NIA`CIp}Y&;bAd9l!;7)h!eO z0LW`3VL$}yj=A;ofV%Y$rC#*t4N6y6RiRUUq%>yyhL#;bOC0C=8WjV)5of5Q%OOjf zp;9wg(Eb*drRJ`gF-D0NQkrEAeLa+7#1^K`rEHn7CzAW{4T&`o1)ytt7->1o z1E3d*kv-YP(%5z^b=GIoSFki7P4;&Q!IhI}-dOl}0%>O;#a1#$`Kdt^l$D?r+Q~yD zDy#~|jm%rkSuSqT8g}!c*-TE*94_*;q-*U(4=zr+&iK~Ri%*67SQ>eI68U!KB3U6y z)C-i7$+THkFNZ8KY*GOTl+I&^Bw*eqhA5)FG?fxb&MQYyJMCxwcz76NPm6A2L1ES@ zYcVmZ8SDownhwMcL~DQhn0bQx^eP@7kpPe-RGj8dNK8mT44y}WE+haT0dj&$3WmDv z2!TSlKZ8M+Eg>3=hjzwqnj1K(ooUUG+j3jN!oxDZy?Mh}#b_&LaNO(9YTPsEx@G6g zn68L}J2RO_widch{6`zJiH6>_uaEV2ayEj@FXv_Tk3z|bb#=hF3BrRK#t?WSI{ad7 zMrzHC6l}_OU4e&q zaZYQ44*3^3R-4gm)C-PEOs#P$bQaM}X>*$vd`_cO6C{0$2P1uj{&)=9Oo0#QwFnEX z^uS?mrk#Lykx=7ZM`IkYL;pLAXRnby?cOFl|T@^J>hkWOFk5bkqWe2(cI|n#l;Kw6sZ@B zo928bnRG%&!rv7$Dg7`PvXw-4Ir^H5fcpJ`dw%#%M?M`N2VoMHa6CAK;gWLZ+wEu^ zJv2TK6oO{oLBoi(#0O=sqi%pRd)r z>g7#=-R8zDqE6jWPMw$?INB669^taroiat>^hQ=B)fxM_-V9i<2Jg){?AoQ|=y@Bj z4@1zdufi1ed2-+T?R}N?=!i9NRU1?{gOr6XKNjr) z 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