Skip to content

Commit

Permalink
Merge pull request #235 from micromata/8.0-Release
Browse files Browse the repository at this point in the history
8.0 release
  • Loading branch information
kreinhard authored Dec 23, 2024
2 parents acdcba0 + cec9dd3 commit 6b190c3
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 61 deletions.
1 change: 1 addition & 0 deletions Migration-ToDo.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
Aktuell:
- Besucherbuch: englische Übersetzung
- AG-Grid: setColumnStates wird nicht in den UserPrefs gespeichert.
- Wicket: Auftragsbuch: org.apache.wicket.core.request.mapper.StalePageException: A request to page '[Page class = org.projectforge.web.fibu.AuftragEditPage, id = 9, render count = 3]' has been made with stale 'renderCount'. The page will be re-rendered.
- -XX:ReservedCodeCacheSize=100m
- Rechnungen: skonto-Datum wird automatisch auf nonsens gesetzt.
- History: Rechnungsposition -> Auftragsposition taucht nicht in History auf.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,11 @@ dependencies {
testImplementation(project(":projectforge-business"))
}

tasks.withType<ProcessResources> {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from("src/main/java") {
include( "**/*.html") // Wicket pages.
}
}

description = "org.projectforge.plugins.liquidityplanning"
7 changes: 7 additions & 0 deletions plugins/org.projectforge.plugins.marketing/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,11 @@ dependencies {
testImplementation(libs.jakarta.servlet.api)
}

tasks.withType<ProcessResources> {
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
from("src/main/java") {
include( "**/*.html") // Wicket pages.
}
}

description = "org.projectforge.plugins.marketing"
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ public void update(final TeamCalDao teamCalDao, final TeamCalDO teamCalDO) {
}
});
} catch (IOException ex) {
log.error(ex.getMessage(), ex);
log.error(ex.getMessage());
return;
}
if (bytes == null) {
Expand Down Expand Up @@ -223,7 +223,7 @@ public void update(final TeamCalDao teamCalDao, final TeamCalDO teamCalDO) {
+ " information, quit from url '"
+ displayUrl
+ "': "
+ e.getMessage(), e);
+ e.getMessage());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

package org.projectforge.business.fibu

import org.projectforge.common.extensions.isZeroOrNull
import org.projectforge.framework.i18n.RequiredFieldIsEmptyException
import org.projectforge.common.i18n.UserException
import org.projectforge.framework.persistence.api.QueryFilter
Expand Down Expand Up @@ -100,6 +101,10 @@ object AuftragAndRechnungDaoHelper {
}

private fun checkAndCalculateDiscountMaturity(rechnung: AbstractRechnungDO) {
if (rechnung.discountPercent.isZeroOrNull()) {
// No discount, so no discount maturity.
return
}
val discountZahlungsZiel = rechnung.discountZahlungsZielInTagen
if (rechnung.discountMaturity == null && discountZahlungsZiel != null) {
val rechnungsDatum: LocalDate? = rechnung.datum
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

package org.projectforge.rest.core

import jakarta.servlet.http.HttpServletRequest
import mu.KotlinLogging
import org.projectforge.common.MaxFileSizeExceeded
import org.projectforge.common.i18n.UserException
Expand All @@ -38,71 +39,79 @@ import org.springframework.web.bind.annotation.ControllerAdvice
import org.springframework.web.bind.annotation.ExceptionHandler
import org.springframework.web.bind.annotation.ResponseStatus
import org.springframework.web.multipart.MaxUploadSizeExceededException
import jakarta.servlet.http.HttpServletRequest
import java.io.IOException
import java.net.NoRouteToHostException

private val log = KotlinLogging.logger {}

@ControllerAdvice
internal class GlobalDefaultExceptionHandler {
@ExceptionHandler(value = [(Exception::class)])
@Throws(Exception::class)
fun defaultErrorHandler(request: HttpServletRequest, ex: Exception): Any {
// If the exception is annotated with @ResponseStatus rethrow it and let
// the framework handle it.
if (AnnotationUtils.findAnnotation(ex.javaClass, ResponseStatus::class.java) != null) {
throw ex
}
if (ex is MaxUploadSizeExceededException) {
val userEx =
MaxFileSizeExceeded(
ex.maxUploadSize,
-1,
maxFileSizeSpringProperty = "spring.servlet.multipart.max-file-size|spring.servlet.multipart.max-request-size"
@ExceptionHandler(value = [(Exception::class)])
@Throws(Exception::class)
fun defaultErrorHandler(request: HttpServletRequest, ex: Exception): Any {
// If the exception is annotated with @ResponseStatus rethrow it and let
// the framework handle it.
if (AnnotationUtils.findAnnotation(ex.javaClass, ResponseStatus::class.java) != null) {
throw ex
}
if (ex is MaxUploadSizeExceededException) {
val userEx =
MaxFileSizeExceeded(
ex.maxUploadSize,
-1,
maxFileSizeSpringProperty = "spring.servlet.multipart.max-file-size|spring.servlet.multipart.max-request-size"
)
log.error("${translateMsg(userEx)} ${userEx.logHintMessage}")
return ResponseEntity.badRequest().body(UIToast.createExceptionToast(userEx))
}
if (ex is IOException && isClientAbortException(ex)) {
log.info { ex::class.java.name }
return ex::class.java.name
}
if (ex is NoRouteToHostException) {
log.error("No route to host: ${ex.message}")
return ResponseEntity("No route to host.", HttpStatus.BAD_REQUEST)
}
if (ex::class.qualifiedName?.contains("NoResourceFoundException") == true) {
log.error("Resource not found: ${ex.message}")
return ResponseEntity("Resource not found.", HttpStatus.NOT_FOUND)
}
if (ex is UserException) {
if (ex.logHintMessage.isNullOrBlank()) {
log.error(translateMsg(ex))
} else {
log.error("${translateMsg(ex)} ${ex.logHintMessage}")
}
return if (ex.displayUserMessage) {
ResponseEntity.ok().body(UIToast.createExceptionToast(ex))
} else {
ResponseEntity.badRequest().body(UIToast.createExceptionToast(ex))
}
}
val additionalExcptionMessage =
if (ex is TechnicalException && ex.technicalMessage != null) " technical=${ex.technicalMessage}" else ""
val exceptionMessage = "${ex::class.java.name}: ${ex.message}$additionalExcptionMessage"
log.error(
"Exception while processing request: ${ex.message} Request: ${RequestLog.asJson(request)},\nexception=$exceptionMessage\n${
ExceptionStackTracePrinter.toString(
ex,
showExceptionMessage = false,
stopBeforeForeignPackages = false,
depth = 15
)
}"
)
log.error("${translateMsg(userEx)} ${userEx.logHintMessage}")
return ResponseEntity.badRequest().body(UIToast.createExceptionToast(userEx))
}
if (ex is IOException && isClientAbortException(ex)) {
log.info { ex::class.java.name }
return ex::class.java.name
log.error(ex.message, ex)
return ResponseEntity("Internal error.", HttpStatus.BAD_REQUEST)
}
if (ex is UserException) {
if (ex.logHintMessage.isNullOrBlank()) {
log.error(translateMsg(ex))
} else {
log.error("${translateMsg(ex)} ${ex.logHintMessage}")
}
return if (ex.displayUserMessage) {
ResponseEntity.ok().body(UIToast.createExceptionToast(ex))
} else {
ResponseEntity.badRequest().body(UIToast.createExceptionToast(ex))
}
}
val additionalExcptionMessage =
if (ex is TechnicalException && ex.technicalMessage != null) " technical=${ex.technicalMessage}" else ""
val exceptionMessage = "${ex::class.java.name}: ${ex.message}$additionalExcptionMessage"
log.error(
"Exception while processing request: ${ex.message} Request: ${RequestLog.asJson(request)},\nexception=$exceptionMessage\n${
ExceptionStackTracePrinter.toString(
ex,
showExceptionMessage = false,
stopBeforeForeignPackages = false,
depth = 15
)
}"
)
log.error(ex.message, ex)
return ResponseEntity("Internal error.", HttpStatus.BAD_REQUEST)
}

/**
* Checks if the exception is a client abort exception.
* @param exception The exception to check.
* @return True if the exception is a client abort exception.
*/
private fun isClientAbortException(exception: IOException): Boolean {
val message = exception.message?.lowercase()
return message?.contains("broken pipe") == true || message?.contains("connection reset by peer") == true
}
/**
* Checks if the exception is a client abort exception.
* @param exception The exception to check.
* @return True if the exception is a client abort exception.
*/
private fun isClientAbortException(exception: IOException): Boolean {
val message = exception.message?.lowercase()
return message?.contains("broken pipe") == true || message?.contains("connection reset by peer") == true
}
}
2 changes: 1 addition & 1 deletion projectforge-webapp/src/components/base/FormatterFormat.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ const formatterFormat = (
case 'KONTO':
case 'PROJECT':
case 'EMPLOYEE':
return value.displayName;
return value?.displayName ?? '???';
case 'SHOW_LIST_OF_DISPLAYNAMES':
if (value && Array.isArray(value)) {
return value.map((obj) => obj.displayName).join(', ');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ protected void cloneData() {
final O data = getData();
getLogger().info("Clone of data chosen: " + data);
data.setId(null);
data.setCreated(null);
data.setLastUpdate(null);
data.setDeleted(false);
}

Expand Down

0 comments on commit 6b190c3

Please sign in to comment.