From ae9b812d8c9b8b9a4a22a1a748acec9a9e9a0419 Mon Sep 17 00:00:00 2001 From: Vishwas Babu A J Date: Fri, 12 Jul 2013 18:32:18 +0530 Subject: [PATCH] bug fixes for mifosx-519, 516, 361 and 362 --- api-docs/apiLive.htm | 6 ++-- .../journalentry/api/DateParam.java | 29 ++++++------------ .../api/JournalEntriesApiResource.java | 7 +++-- ...WritePlatformServiceJpaRepositoryImpl.java | 12 +++++--- .../core/serialization/JsonParserHelper.java | 3 +- .../ReadWriteNonCoreDataServiceImpl.java | 27 ++++++++++++++-- .../main/pentahoReports/Income Statement.prpt | Bin 16882 -> 16888 bytes 7 files changed, 49 insertions(+), 35 deletions(-) diff --git a/api-docs/apiLive.htm b/api-docs/apiLive.htm index 6b45552fe..655a097bd 100644 --- a/api-docs/apiLive.htm +++ b/api-docs/apiLive.htm @@ -15300,14 +15300,14 @@ No Request Body:
fromDate
- Date (yyyy-MM-dd)optional + Dateoptional
Filters for Journal entries whose entry Date is greater than or equal to the passed in Date
toDate
- Date (yyyy-MM-dd)optional + Date optional
Filters for Journal entries whose entry Date is lesser than or equal to the passed in Date @@ -15322,7 +15322,7 @@ No Request Body:
journalentries?transactionId=PB37X8Y21EQUY4S

-
journalentries?officeId=1&glAccountId=17&manualEntriesOnly=true&fromDate=2012-01-01&toDate=2013-01-01
+
journalentries?officeId=1&manualEntriesOnly=true&fromDate=1 July 2013&toDate=15 July 2013&dateFormat=dd MMMM yyyy&locale=en

journalentries?fields=officeName,glAccountName,transactionDate

diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/DateParam.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/DateParam.java index 67ba484d9..f0b051754 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/DateParam.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/DateParam.java @@ -5,16 +5,13 @@ */ package org.mifosplatform.accounting.journalentry.api; -import java.text.DateFormat; -import java.text.ParseException; -import java.text.SimpleDateFormat; import java.util.Date; +import java.util.Locale; import javax.ws.rs.WebApplicationException; -import javax.ws.rs.core.Response; -import javax.ws.rs.core.Response.Status; -import org.apache.commons.lang.StringUtils; +import org.joda.time.LocalDate; +import org.mifosplatform.infrastructure.core.serialization.JsonParserHelper; /** * Class for parsing dates sent as query parameters @@ -23,23 +20,15 @@ import org.apache.commons.lang.StringUtils; */ public class DateParam { - private final Date date; + private final String dateAsString; public DateParam(final String dateStr) throws WebApplicationException { - if (StringUtils.isEmpty(dateStr)) { - this.date = null; - return; - } - final DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); - try { - this.date = dateFormat.parse(dateStr); - } catch (final ParseException e) { - throw new WebApplicationException(Response.status(Status.BAD_REQUEST) - .entity("Couldn't parse date string: Expected format yyyy-mm-dd " + e.getMessage()).build()); - } + this.dateAsString = dateStr; } - public Date getDate() { - return this.date; + public Date getDate(String parameterName,String dateFormat, String localeAsString) { + Locale locale = JsonParserHelper.localeFromString(localeAsString); + LocalDate localDate = JsonParserHelper.convertFrom(dateAsString, parameterName, dateFormat, locale); + return localDate.toDate(); } } diff --git a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/JournalEntriesApiResource.java b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/JournalEntriesApiResource.java index 79ac08216..6c2217b46 100755 --- a/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/JournalEntriesApiResource.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/accounting/journalentry/api/JournalEntriesApiResource.java @@ -78,17 +78,18 @@ public class JournalEntriesApiResource { @QueryParam("fromDate") final DateParam fromDateParam, @QueryParam("toDate") final DateParam toDateParam, @QueryParam("transactionId") final String transactionId, @QueryParam("offset") final Integer offset, @QueryParam("limit") final Integer limit, @QueryParam("orderBy") final String orderBy, - @QueryParam("sortOrder") final String sortOrder) { + @QueryParam("sortOrder") final String sortOrder, @QueryParam("locale") final String locale, + @QueryParam("dateFormat") final String dateFormat) { this.context.authenticatedUser().validateHasReadPermission(this.resourceNameForPermission); Date fromDate = null; if (fromDateParam != null) { - fromDate = fromDateParam.getDate(); + fromDate = fromDateParam.getDate("fromDate", dateFormat, locale); } Date toDate = null; if (toDateParam != null) { - toDate = toDateParam.getDate(); + toDate = toDateParam.getDate("toDate", dateFormat, locale); } final SearchParameters searchParameters = SearchParameters.forJournalEntries(officeId, offset, limit, orderBy, sortOrder); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/codes/service/CodeWritePlatformServiceJpaRepositoryImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/codes/service/CodeWritePlatformServiceJpaRepositoryImpl.java index 0ec156924..52807c4eb 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/codes/service/CodeWritePlatformServiceJpaRepositoryImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/codes/service/CodeWritePlatformServiceJpaRepositoryImpl.java @@ -49,12 +49,13 @@ public class CodeWritePlatformServiceJpaRepositoryImpl implements CodeWritePlatf context.authenticatedUser(); this.fromApiJsonDeserializer.validateForCreate(command.json()); - + final Code code = Code.fromJson(command); this.codeRepository.save(code); return new CommandProcessingResultBuilder().withCommandId(command.commandId()).withEntityId(code.getId()).build(); } catch (DataIntegrityViolationException dve) { + handleCodeDataIntegrityIssues(command, dve); return CommandProcessingResult.empty(); } } @@ -65,9 +66,9 @@ public class CodeWritePlatformServiceJpaRepositoryImpl implements CodeWritePlatf try { context.authenticatedUser(); - + this.fromApiJsonDeserializer.validateForUpdate(command.json()); - + final Code code = retrieveCodeBy(codeId); final Map changes = code.update(command); @@ -112,9 +113,10 @@ public class CodeWritePlatformServiceJpaRepositoryImpl implements CodeWritePlatf */ private void handleCodeDataIntegrityIssues(final JsonCommand command, final DataIntegrityViolationException dve) { Throwable realCause = dve.getMostSpecificCause(); - if (realCause.getMessage().contains("code_name_org")) { + if (realCause.getMessage().contains("code_name")) { final String name = command.stringValueOfParameterNamed("name"); - throw new PlatformDataIntegrityException("error.msg.code.duplicate.name", "A code with name '" + name + "' already exists"); + throw new PlatformDataIntegrityException("error.msg.code.duplicate.name", "A code with name '" + name + "' already exists", + "name", name); } logger.error(dve.getMessage(), dve); diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java index f3b51db00..50ba29431 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/core/serialization/JsonParserHelper.java @@ -338,7 +338,7 @@ public class JsonParserHelper { return value; } - public LocalDate convertFrom(final String dateAsString, final String parameterName, final String dateFormat, + public static LocalDate convertFrom(final String dateAsString, final String parameterName, final String dateFormat, final Locale clientApplicationLocale) { if (StringUtils.isBlank(dateFormat) || clientApplicationLocale == null) { @@ -518,6 +518,7 @@ public class JsonParserHelper { } } + /***TODO: Vishwas move all Locale related code to a separate Utils class***/ public static Locale localeFromString(final String localeAsString) { if (StringUtils.isBlank(localeAsString)) { diff --git a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java index 4969eaf31..01f908e12 100644 --- a/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java +++ b/mifosng-provider/src/main/java/org/mifosplatform/infrastructure/dataqueries/service/ReadWriteNonCoreDataServiceImpl.java @@ -162,6 +162,7 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ public void registerDatatable(final String dataTableName, final String applicationTableName) { validateAppTable(applicationTableName); + assertDataTableExists(dataTableName); final String registerDatatableSql = "insert into x_registered_table (registered_table_name, application_table_name) values ('" + dataTableName + "', '" + applicationTableName + "')"; @@ -214,6 +215,17 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ String[] sqlArray = { registerDatatableSql, permissionsSql }; this.jdbcTemplate.batchUpdate(sqlArray); + } + /*** + * Strangely, a Hibernate contraint violation exception is thrown + ****/ + catch (ConstraintViolationException cve) { + Throwable realCause = cve.getCause(); + // even if duplicate is only due to permission duplicate, okay to + // show duplicate datatable error msg + if (realCause.getMessage().contains("Duplicate entry")) { throw new PlatformDataIntegrityException( + "error.msg.datatable.registered", "Datatable `" + dataTableName + + "` is already registered against an application table.", "dataTableName", dataTableName); } } catch (DataIntegrityViolationException dve) { Throwable realCause = dve.getMostSpecificCause(); // even if duplicate is only due to permission duplicate, okay to @@ -221,7 +233,6 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ if (realCause.getMessage().contains("Duplicate entry")) { throw new PlatformDataIntegrityException( "error.msg.datatable.registered", "Datatable `" + dataTableName + "` is already registered against an application table.", "dataTableName", dataTableName); } - logAsErrorUnexpectedDataIntegrityException(dve); throw new PlatformDataIntegrityException("error.msg.unknown.data.integrity.issue", "Unknown data integrity issue with resource."); @@ -296,6 +307,14 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ return new Boolean(isRegisteredDataTable); } + private void assertDataTableExists(String datatableName) { + final String sql = "select if((exists (select 1 from information_schema.tables where table_schema = schema() and table_name = ?)) = 1, 'true', 'false')"; + final String dataTableExistsString = this.jdbcTemplate.queryForObject(sql, String.class, new Object[] { datatableName }); + final boolean dataTableExists = new Boolean(dataTableExistsString); + if (!dataTableExists) { throw new PlatformDataIntegrityException("error.msg.invalid.datatable", "Invalid Data Table: " + + datatableName, "name", datatableName); } + } + private void validateDatatableName(final String name) { if (name == null || name.isEmpty()) { @@ -932,7 +951,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ if (appTable.equalsIgnoreCase("m_product_loan")) return; if (appTable.equalsIgnoreCase("m_savings_product")) return; - throw new PlatformDataIntegrityException("error.msg.invalid.application.table", "Invalid Application Table: " + appTable); + throw new PlatformDataIntegrityException("error.msg.invalid.application.table", "Invalid Application Table: " + appTable, "name", + appTable); } private String mapToActualAppTable(final String appTable) { @@ -1188,7 +1208,8 @@ public class ReadWriteNonCoreDataServiceImpl implements ReadWriteNonCoreDataServ } if (columnHeader.isDateDisplayType()) { - final LocalDate tmpDate = helper.convertFrom(paramValue, columnHeader.getColumnName(), dateFormat, clientApplicationLocale); + final LocalDate tmpDate = JsonParserHelper.convertFrom(paramValue, columnHeader.getColumnName(), dateFormat, + clientApplicationLocale); if (tmpDate == null) { paramValue = null; } else { diff --git a/mifosng-provider/src/main/pentahoReports/Income Statement.prpt b/mifosng-provider/src/main/pentahoReports/Income Statement.prpt index f4c88f7a226f7b166a9ee840fe7080741eb4501f..464508a774d33badc0faf08e7f77ae7083c01084 100644 GIT binary patch delta 5266 zcmZ{o2{@Er)WB!#J87&lG(rrr?>pH-jI9z`qm;5|H)YSxt0<8*TWE|dStCo9tdZ=p zC5G&~Z}i8%f8Y1r=Xsy!y!W1a&bjw}pWiw6t^|O$0>G3=Eqnqp5Qqc>nzmVprMyf; zLUJUbO#v|KA5j_fN5koX?%TLp@_XF3e`h$N6Cenw88Z+a?$w{^6RWd|;2&ko*aE$K ziDtf&RLUn`vlkImkyB}0e?_-*)7@FKcqfmy1pjB|RXar|2pp-*p#RivIYlA3>;q2{ zrxzv`?|Pc5LBNV7Wl9Ep9ts96>`6t3*-(s?f3H}ks9@=vy@8wv$+CVAk=w94E?;E9>%Wvzn9o?w2LlGYAjjoPS-6?A{ z^KwAb*qiowGLDo~z7XwAyg4GM8KTMpeebRk#o9CBv1Z+3JBHLECc*EZn05n$K-UOB zprcO+0g?RkwSZLo)#Go7c}}2#n|ALT?t2 zRkPU}Z@1tL9MZA7w(?=HWW7cpd@>ImO7Cs$%leMlnj!JtS`a#w!X+su;F zm>=U^I=A~7UsUt_?wckxiur&ZUz+E1v-Or6hZPnn>^s35hy3=nSkkSP z=z-dUHTc|t<$Q)~`PldFZRO_`$mjWQ7UU?Om{C|M>G|xgtn9f-X6mKu>eVG(l}=Rv zvsRjsMvqfk^?hYptfDNsh&QU)EObd^ZoCb>_>BTdO4(Nq7gJKHtrNOsw`ENs^^*6U z7*X0iB8?p_%dA9RS8yyNz?_z~58}OS>q3Br=%dNHQpQ8S-iTSSRjky}G1bZDD_7*W zU1AB;kwdgwOB{wt$g~jGDSnL07^)~oX{AvqR5L{={3z%%vy+sCKNnuEMCZv%NuI&r8%5<@OKy5|R}+iaC{avp`1IBWW@J~sTclAa7GYls zQ%@yJXUrUs#s`|7)ztiwt_@f&-8Tf9$%uqE`u5A$Md8;0%2<6ZRLkQU;rxtGCO4F)Lc~ca6?D17 zvydxAj5)&O#z7OGB`QOBt*6u1X|TRzY>$!j_0Dp3A`=$}U;IoXK4(0mYLUnUASt;~ z%(=6UM2Kde$MPM0+{4=K{gV>;EgWR5yZtm`TM z^0@NHTVzRwObHtX%gcUh^B-13$EE{O4IPqZWIgIJPD&j2x;>bOyvx8PR!=g6(J&gp z3#Jz|UbzNfN1hF7i9WKi6#Eo2fxY&A4l%kYaK|)nV|IBOu4#v?ep8$Z;lH7tfSeAH zzp4E?0IS<88W-$IcQxfrr@W^OwdB(HIeEl4xdqo}Fgg0sUFEhE|5VF2uV7tQWPIy7 z?d6z6sl9vHe&93tXjfG(?PjqSZ2D=oUqIm>L1>7u#lH*(AWMqBAcQHRswO;Pj5B2O zSj0Hc{lG)b)|@?@nQ1bgXl{G$iR}g$FsXE3jH-d98t20hI#$w}s}c=m)ZqhGV}=Sf zGD^-qy8GBwL8ks$%bxolhq?`{*%Q2E2AMM^$qPK&4TV>M)rzr#E3?V6A^GoizvY{& zZViYyc8`INB7C^}WK$d;1j?s6jqoI_5yt(Ku*RA4PT?2>RXow?hc=yvdVElz8paP6 z0~TP+jEh%Y@x#*8^t7rfXR#jc)IVgo%fFx9y$Z0h9HJOdRXdB@W?hSoU)7zQDw_jV zgZf-KG-e^P%!%r2tl^7F7(C(k_wxm$r zJzu9cmu!?ki4|UnTvQy?ccjIXFGD!!*@8F|8XQJTzp;p^adXi@SyQ#|Zd8gFOER($ z{$L{?E7=6IRYgaon`@KYgf5xfH4Bue){oFGzMY8Yodd9(lLljf>*shVCSCb|`^ZK?thdCA&j9T~FKd9>q#f!ZSS{M|ef+1P_HQ z*aW8ct>b1xo$!#ROyzP0b;hckCAG`g1|)Z)RdY0&XEk13B&zos@taJ6Ok_PJqRXI~ znR6*7u!~xlluF?W_Y=Z|$xfP_KJwVz2_0{2TC$Lpp43dIF9n;w1^sZB-+7S_oZs-~ zB>)Y$kWI6O&}{>H7j1^|No}`!KdVAMGw72GO3P@IOI;RY>rqj0knPN730DLceAAXTp<-yGpJbUa6Dj_##Rd8=<5LbpKiO%|YardBU{;#wmC(E&GJ0Uro&HmTD_w_x3wf47dNKq5cLCW0$xr!AGhec)Z!= zvdtSpKO63E-I|v2YtH6&yuM+at@qs&LMqg4#`0c%!rDaF!GXtZyy7EoF}Es~sfD)( zAyy@`Bba%3Se3fl2^$W3G1ny1`b&tvdNb4GdvfnVM+U`a{T%HG9+86Tx>V0yv+C@M zALg&iP->s*@7xlTEv$dQZQ!=9&LAd%tJde?zWDB}7Gk{M z&<6bdb0k0 zHIDs^mPVQ{Z6g(G+X)Yb>!8qsxxq#mt8-f)O}d2ot2qlBwX};cvzmRz4*Q=Rp6wAH zu_8F@T6iBJ2t*1wWyNtx#fyMqM$U5(oX~(50mHC!;o<%JE`Kay91y2<7RHCC^Asz3 ze3~cPX?$HLqWeWTjYEgC5|G3F12SIad&OezkgMWZ9JB!s>n!ZE?tIgI>axi{JBgtl zN)WxYx8&WqaT5?1q)8ILq7pj1Er%N2j7}aEID(HF4nCszyQ3b+!AzwnDaxu_SGi?P4LXm6>m!1fJ%3Vu{J|qn_=xds0=g%DyZ{_GJl|d zAVN8X>IS*3pva9%lHN}W<@po+g(V0g5oCq21Xb84HkgmQ$I_5Y$QX*O=T^@n=c=CX zNG5Fr|3rg@8rV-9@`MP5xe=czj5Muq0&_Y{a|KnNeGZu&uHSkkqU1(3=R0^PUpDey zX?(V}c5i)9=U-I*`U{m_PJ7GS0SSI*)JYYLn?CbcG97$xR~pg4K9cGn%XwG)d24`- z(UeR}9#d>I`YfyZ1eGWpD=iXYsd23Q5E3sJvvODDdmj8zPVJQ1as|Q9GiWPiaDL3W zubShfQi3&8%5h|uQsB{dB-96!=-Z{4(TKvhaRN{zTSFj{!vbPj;Jy1H;;MI)>C!{D zZi{EJ4)(;Y&-{VJ#-L_6ngqdD0rOjb{RNvNq%4*D)sfmpAB7n!iESL6Z*g*sUoUlBM{uhr%Df?-x}S4pA55(4h#zidOOSL% zMJ@M=z=HaY#6V&FiYAX{!r2uzk6t5gFGD0dviW0=mAiDgtNj9bsWCo9Wxh9`rxMhi zUhdH+w`+NrFlH3kr8+&Os#~?jWYDK$a#>f|EAeqxd}l?ylc4?NW1XS`eU>iihiXdu z-?b*KBe{h#Usss+PLW?V@#(Vr-@P?U5w(kS>0(m;`BiAa)5gV289E)^Vr7J{|)L--AAByhz;zb z`WMvEjNS5DHcAn7c@epIh=?1j+gdLjMx!T3hTUcz!ogXFib67yM#B0gSJ zT2A{7^|j#NP&c9W_CIVqfqJpv!u_;G_fx1x{f4?MLhkSk&>MgL@pUEjoS#FDL@`Ybzy4s2P zZI}S~3p_KxGa|6(??e!NlnD56iC~Mi^ITUH*b|ab@;PuqNP4E$j9}P4Ly;RQfB0arTawHLpYU>ny8x>v zJObdpCGehB?lcw5b#GH9v;_DbX95^WC`%9FGaV<-sD11H-J#`At-zjGQhJ@;;5(T} z=Gk(E zCiU{Q&zQ-AGM!zCXyIYf3@uUexjo32*_M3I^(dlj)N|H|xS(#0dmd(u?OYzo%~2cV zYw}@~v9dDqQq}@y$t0oHdVA8l>{i(W@+Z06_YL|O> zQ0c`fJf&`>6={GkTZq8aTUDxTGhIKQJ*kDa-P-0JljgiL>MKnoZTgcc_x3t$_?IP! z%zBp-KD$-1Z=~o}?bwePP2Q*~Uz3)>Dve+ZYF9hr6n+5k zEaOQmNA)pqC`&JOiw*>GarLyfaXGr#L27~VAfVGb1!cI&cy=AmE-miH0jKy!>j8A+ z*v=mZ!tfz9*A}Oug{!z>e<*=KUw~LS?vp5L<)C;$Pyh{MNAF+k8v?P#qB1}=^xF72WNp9IAo#%-pZU>Ayii#wsVmI@ delta 5160 zcmZu#cOcaN|3CArL&!RNWRDQCN0B?p$SPYzDKg@$<0NGFR>+o%va(9{NID`b&Yt12 zmA!uSaee!KzQ5NWcaM8L$NTwuzwY&XJYUc45Q6m(0*F4Gh?oL&GWf4y;~_evAhHuw zVgV2!|A896rKcZc$KUrHJZ;5&?zvQ4AJuE-VyGK8iW?(Bd)&bcwJ=-OoY$Ho;!+-# z6r;_IjCDmOc3d9qcsk}LKR%Ah`q^S?oXsv@9M#FAHANQu{FS~haCs$P+aD^nju|N! zfDuYW6^L{Zn9>s8$s9udJQR++ll$R}6{6@eNjex4O`@!^Z7a9(Xcq?e60cfP{>Ug5 z`HtI6?{%T2PSkLhhM`~<38IbcrGbjN6IB;o`P{3X2lv!M=o>5d(FEaz>cuwElvn+K zWy-;bsw6_UVA(=CS;+$8mx7cc%uy*wiS$CdPq(AW7?f)Z zNm0{d>6iK%Mb%aP>Wr)c5+rj*2ez_Cot|UI0_B45qfD->GE`Ar-(*q1XkF=g_!!}K z;R`NeD1vgqrC|)k*PI;(^Jn$uHtp4f*I&u7H^4=j>mHS0VHvjFPq^MQm3}^aht;8B zq{OsdPMg|h6)?@0k^f!>?KG>;v)9~CiSq&b(BNsuA$Wk2!)}!R9;HYn#RpO%) zeBd0*kXi@q5OuQ(L?J~*B%WXU>599^?zlE=y_QeCYMGym_9h=^of6;o)c&T(p3%H0 z&QuncP`6>BC76nQ%Db^G2Lp=>jNNvw1z%s9*e?=#MAvm?YYs$GSeB4HAPLsHO&`=d zku6mfPpOZzixFP9G!CzoPw`cpD985pm?4~HGNN-Up25qd-IFAW{Kvcy@6Y|(PjL&p zu_(Nh1TpC-`hhSey$91shUN9kTdVkO?)=oCf$`g$jFv1G;<|k!#M8$vS`+2^%d)c# z;l8j=(|-{}_Pt^1EQ~75mF|0Hxy7-t&YThGwZD&Smx!r9WX>sFH1O+wAu6TGkLLAF z>XdIpLRzwM1pwq3RYjLde#x1T-8@guOO7oc+>#EE;z@Jiby)}Xl*7e-gmwD|9TKW4 zWL3zdT|xHcEg#Q1eMj-@v%(^N*EF-4Ld^6Timj~D>&e4pV1SN6!?1}$rZoXawH0~n z1jMcn%d%7hDHbCfgSB#NO3zQUl`IWW>61hH-g8}0zx2L=`jLeceNA{=`oZqZS#T2IeHFITiujqi534bVWz zp!>P}{sQo}*L^D-KTB{Op%q zjNNuH6PQuI>oFz80fCBtSjYBFYeaigkN zk$a}!H%TG-=4QH5Gw=75nGQZi9*{ub-wqk-sIn72^|cFA!u;D6s)B_%`*0>w?4HP^ zSMf5K{I@H&0yZ(#h#&;bR-j>$UnWiz;Uii^^D#l0M4i`6^j^DJ*+%7DtFQ zMUIb*Z0k%FuTDGkDs9bUBF(#={CG2l@wWbO)JzdeY@MGnIULqG$*iF7_kR#QOTfjTV5PgA>^vR(!w{WBBwIi(>vGhwb*{ zkgE6*0*@DG_3t{Qtree@tYmnu;pmWGBLaVSceFHB4Qh8=}5*#;WM)?K-cTH?ewzV#qC3NNH9jixjwT4l;rE^Jsf&eF{ z0>LbBgHxDlFyqmZEvbf4-3&!85XZ?3X;g^X%hAr{Z>xpxiUAdzoJ>8TiO&s$-MBuq zO$t*ffo`%H40TvGs{sp~ELu_8(lNcJ;DQ{99Q`JUP?u3HEB{J-NEeM1IkoCl!3dJX zsaV>)KFavLNj<;ithDoX1{Y^z1E{!BHjE=(fjrWU{6jz912C@h4!Kv(-!@|O&}EvK z()DTzv?~%FV~#kdzFGp;?6Mi(h>3X`{hZ4>);I8|WgacqK&AbfWLkbV@7afZdK6=c%)Z7kUl#W1*X$EYw){8$7gxa5%xvAD%u1B4gK zH?KtC+~m*Uny)0PlQyab*mNHsFnLTK{SH>7i~R1|FAV#htWb0jV7q12U@?KHi2ES! z)+(k*77Gqb3vZ|m;&4Lu<<`6H7Z-SSUfRvgWT^3psqrwd^FsBg!o z^z|##I-BYh7G8yVMmpqWH>Y7=W)RWPL*g7((J`PBl2M@4xU$VaqSvOf zAaf@*l82jhC|}c5z~5y7w{#Uvt(54a%!vN1eBg6gA^WknHWGq6N@w!iNe;Pb03P+Z zc2VUMnkM~>YmN6%JtAS!irF&zX2V2oo{F|!M?Q^=%t8CFenF%;b9BPv+s4=>Vu^L^ zmxDdQP0jTw%o>Z|#_S?S$nWsS%-q+aamOQnA(EPndQqEHEU`1#;|T9kCdt<9$?Z%3aoL4S1a-yUtZnDB~Pz!^|BbM54)cjo9!_K<);NA!P=`e{8RAp|X( zLG-<{oZu*5>w0o#YGT77b z+oLntY?t41#J$gw-SA^mYJrIQCZBQ9qGdtL)|PY5k6Tnzw2QnJwAA|?HgVN7Yu-RT zT8*6^9@&9At`kcnu#QNG<^Pj6&_UCVp&XHL$y)+H{(^pVU(BUZ|5}O=+g1)nVaUzW1VR2z>TR>jpVXv=VT$J4oz>lg>2Je6xcaU4 zxjFE!Xg@=Iddbp?%O^7FVKNk)d^|IsY!&_V{bzM2a58aN#<_%#DEOM6+w~uzpAXz} zmACXAqs*?y?AUW!hI28K$R%96b*{3hcGqiIlIe}^rOek->ssI(9gy3tOj1)E3b|-Q zOBea(e1wa!x>MiPQjIi2<^|`hEA$`jCmDohUE1uTaD*1Aul2CkzHG#pYE3=o@)<{S zJH0LT)ojIR!Q%>7{EkaFJS&t+F5b&p^8ObqCo2;27!k2jL4ds^zPoehuMI5kSFmO@1H%B*fDvCSxaKj9on8oB`po-C#{|jf9BPw zs4Mqj>4DX?dt-B`G`F`I7L$$l*M3p+q#6jUmkea`_D`(=ikl9Gtd;`J%e6rt8i?+4 zdsy1E;hCcg&m12pk!h+xi~ zC=SaJw+JQq@_WiuK?L`jgHM>lZj>RB39EF<9L+0O29lVzx>4u8O4`dBO68_u%X-_` z{@&^o7`u=7TA(N_-tTbjiKiAMRtrguJF;T%+&f_o%28kVc@<<6+B==jwbttob(pKo zolu8iN4~erg=6aI6g*~*)W>1QU;I&~2$$RSYAY|Xw3srW*JE?)+YW}sZ+8lFEyJRr zgJY-o`fBG<7K)d#RZ{)j80#-*xs1uP$$~mJzQ-E%8@0Ual$G-={W`On%=Yf{!z>dKdMMt^7skGY*Pw57xqR|6` z9t1xgx*mADKHn!eArO8CGR47T0{Qhn1X3@@U)C!7@U(PJlE8 zNjx+3DkxK)_D)Xj=>Sww1TWH6l*YqQMFl);QIy3)0wqn_Q`sc3DgsYe5NgGBVPC&>2`qKc=;4-x*arXLU?1lv&WX62p5MFJF)+LsM0x=2zsq`n5H!4u#3LX#$P*!KhJ6}9@z6+44a^WY$RiT6&Lcn=-Mw)-c3%DJN z?0$=YU1rL^7g>8OaTPu>0#;R_cvolCpoEu1f6r!$Q33jDe