Skip to content

Commit

Permalink
#476 import billing document (#563)
Browse files Browse the repository at this point in the history
* #476 - backend code

* require PostgreSql >= 9.6

* re-enable Gradle 5.0-RC5. See https://youtrack.jetbrains.com/issue/IDEA-201974
The issue has been solved in the build 183.4588.3 (2018.3.1) which is EAP at the moment.

* allow tests to run offline

* #476 - frontend

* update gradle to 5.0

* #476 - send credit note PDF via email

* #476 - generate and save credit note

* fix #543 - add external reference to billing document
this is a placeholder for now, we'll improve the APIs later on

* #476 - add organizationId

* #476 - reset VAT, regenerate Billing Document
  • Loading branch information
cbellone authored and syjer committed Dec 17, 2018
1 parent bb001b3 commit 7ffd466
Show file tree
Hide file tree
Showing 62 changed files with 1,457 additions and 436 deletions.
5 changes: 0 additions & 5 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,6 @@ install:
- TERM=dumb ./gradlew -q assemble
matrix:
include:
- jdk: oraclejdk8
env: PROFILE="-Dspring.profiles.active=travis -Ddbenv=PGSQL-TRAVIS -Dpgsql9.5"
dist: trusty
addons:
postgresql: "9.5"
- jdk: oraclejdk8
env: PROFILE="-Dspring.profiles.active=travis -Ddbenv=PGSQL-TRAVIS -Dpgsql9.6"
dist: trusty
Expand Down
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -288,9 +288,9 @@ jacoco {
jacocoTestReport {
group = 'Reporting'
description = 'Generate Jacoco coverage reports after running tests.'
additionalSourceDirs = project.files(sourceSets.main.allSource.srcDirs)
sourceDirectories = project.files(sourceSets.main.allSource.srcDirs)
classDirectories = project.files(sourceSets.main.output)
additionalSourceDirs.from(project.files(sourceSets.main.allSource.srcDirs))
sourceDirectories.from(project.files(sourceSets.main.allSource.srcDirs))
classDirectories.from(project.files(sourceSets.main.output))
reports {
xml.enabled = true
csv.enabled = false
Expand Down
Binary file modified gradle/wrapper/gradle-wrapper.jar
Binary file not shown.
3 changes: 1 addition & 2 deletions gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
#Sat Nov 24 12:27:25 CET 2018
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.0-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip
2 changes: 1 addition & 1 deletion gradlew
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`

# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
DEFAULT_JVM_OPTS='"-Xmx64m"'

# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
Expand Down
168 changes: 84 additions & 84 deletions gradlew.bat
Original file line number Diff line number Diff line change
@@ -1,84 +1,84 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################

@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal

set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%

@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m"

@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome

set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init

echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe

if exist "%JAVA_EXE%" goto init

echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.

goto fail

:init
@rem Get command-line arguments, handling Windows variants

if not "%OS%" == "Windows_NT" goto win9xME_args

:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2

:win9xME_args_slurp
if "x%~1" == "x" goto execute

set CMD_LINE_ARGS=%*

:execute
@rem Setup the command line

set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar

@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%

:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd

:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1

:mainEnd
if "%OS%"=="Windows_NT" endlocal

:omega
7 changes: 7 additions & 0 deletions src/main/java/alfio/config/DataSourceConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package alfio.config;

import alfio.config.support.PlatformProvider;
import alfio.manager.FileDownloadManager;
import alfio.manager.UploadedResourceManager;
import alfio.manager.system.ConfigurationManager;
import alfio.util.TemplateManager;
Expand Down Expand Up @@ -163,6 +164,12 @@ public JMustacheTemplateLoader getTemplateLoader() {
return loader;
}

@Bean
@Profile("!"+Initializer.PROFILE_INTEGRATION_TEST)
public FileDownloadManager fileDownloadManager() {
return new FileDownloadManager();
}

@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
Expand Down
75 changes: 21 additions & 54 deletions src/main/java/alfio/controller/InvoiceReceiptController.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,45 +20,34 @@
import alfio.manager.FileUploadManager;
import alfio.manager.TicketReservationManager;
import alfio.model.Event;
import alfio.model.OrderSummary;
import alfio.model.TicketReservation;
import alfio.repository.EventRepository;
import alfio.util.Json;
import alfio.util.TemplateManager;
import org.springframework.beans.factory.annotation.Autowired;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Function;

import static alfio.util.FileUtil.sendPdf;

@Controller
@RequiredArgsConstructor
public class InvoiceReceiptController {

private final EventRepository eventRepository;
private final TicketReservationManager ticketReservationManager;
private final FileUploadManager fileUploadManager;
private final TemplateManager templateManager;

@Autowired
public InvoiceReceiptController(EventRepository eventRepository,
TicketReservationManager ticketReservationManager,
FileUploadManager fileUploadManager,
TemplateManager templateManager) {
this.eventRepository = eventRepository;
this.ticketReservationManager = ticketReservationManager;
this.fileUploadManager = fileUploadManager;
this.templateManager = templateManager;
}

private ResponseEntity<Void> handleReservationWith(String eventName, String reservationId, BiFunction<Event, TicketReservation, ResponseEntity<Void>> with) {
ResponseEntity<Void> notFound = ResponseEntity.notFound().build();
return eventRepository.findOptionalByShortName(eventName).map(event ->
Expand All @@ -67,53 +56,31 @@ private ResponseEntity<Void> handleReservationWith(String eventName, String rese
).orElse(notFound);
}

private Optional<byte[]> buildDocument(Event event, TicketReservation reservation, Function<Map<String, Object>, Optional<byte[]>> documentGenerator) {
OrderSummary orderSummary = Json.fromJson(reservation.getInvoiceModel(), OrderSummary.class);
Optional<String> vat = Optional.ofNullable(orderSummary.getVatPercentage());
Map<String, Object> reservationModel = ticketReservationManager.prepareModelForReservationEmail(event, reservation, vat, orderSummary);
return documentGenerator.apply(reservationModel);
}

private static boolean sendPdf(Optional<byte[]> res, HttpServletResponse response, String eventName, String reservationId, String type) {
return res.map(pdf -> {
try {
response.setHeader("Content-Disposition", "attachment; filename=\"" + type+ "-" + eventName + "-" + reservationId + ".pdf\"");
response.setContentType("application/pdf");
response.getOutputStream().write(pdf);
return true;
} catch(IOException e) {
return false;
}
}).orElse(false);
}

@RequestMapping("/event/{eventName}/reservation/{reservationId}/receipt")
public ResponseEntity<Void> getReceipt(@PathVariable("eventName") String eventName,
@PathVariable("reservationId") String reservationId,
HttpServletResponse response) {
return handleReservationWith(eventName, reservationId, (event, reservation) -> {
if(reservation.getInvoiceNumber() != null || !reservation.getHasInvoiceOrReceiptDocument() || reservation.isCancelled()) {
return ResponseEntity.notFound().build();
}

Optional<byte[]> res = buildDocument(event, reservation, model -> TemplateProcessor.buildReceiptPdf(event, fileUploadManager, new Locale(reservation.getUserLanguage()), templateManager, model));
boolean success = sendPdf(res, response, eventName, reservationId, "receipt");
return success ? ResponseEntity.ok(null) : ResponseEntity.<Void>status(HttpStatus.INTERNAL_SERVER_ERROR).build();
});
@PathVariable("reservationId") String reservationId,
HttpServletResponse response) {
return handleReservationWith(eventName, reservationId, generatePdfFunction(false, response));
}

@RequestMapping("/event/{eventName}/reservation/{reservationId}/invoice")
public ResponseEntity<Void> getInvoice(@PathVariable("eventName") String eventName,
@PathVariable("reservationId") String reservationId,
HttpServletResponse response) {
return handleReservationWith(eventName, reservationId, (event, reservation) -> {
if(reservation.getInvoiceNumber() == null || !reservation.getHasInvoiceOrReceiptDocument() || reservation.isCancelled()) {
@PathVariable("reservationId") String reservationId,
HttpServletResponse response) {
return handleReservationWith(eventName, reservationId, generatePdfFunction(true, response));
}

private BiFunction<Event, TicketReservation, ResponseEntity<Void>> generatePdfFunction(boolean forInvoice, HttpServletResponse response) {
return (event, reservation) -> {
if(forInvoice ^ reservation.getInvoiceNumber() != null || reservation.isCancelled()) {
return ResponseEntity.notFound().build();
}

Optional<byte[]> res = buildDocument(event, reservation, model -> TemplateProcessor.buildInvoicePdf(event, fileUploadManager, new Locale(reservation.getUserLanguage()), templateManager, model));
boolean success = sendPdf(res, response, eventName, reservationId, "invoice");
return success ? ResponseEntity.ok(null) : ResponseEntity.<Void>status(HttpStatus.INTERNAL_SERVER_ERROR).build();
});
Function<Map<String, Object>, Optional<byte[]>> pdfGenerator = model -> TemplateProcessor.buildInvoicePdf(event, fileUploadManager, new Locale(reservation.getUserLanguage()), templateManager, model);
Map<String, Object> billingModel = ticketReservationManager.getOrCreateBillingDocumentModel(event, reservation, null);
byte[] res = pdfGenerator.apply(billingModel).orElse(null);
boolean success = sendPdf(res, response, event.getShortName(), reservation.getId(), forInvoice ? "invoice" : "receipt");
return success ? ResponseEntity.ok(null) : ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
};
}
}
6 changes: 3 additions & 3 deletions src/main/java/alfio/controller/ReservationController.java
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ public String validateToOverview(@PathVariable("eventName") String eventName, @P

CustomerName customerName = new CustomerName(contactAndTicketsForm.getFullName(), contactAndTicketsForm.getFirstName(), contactAndTicketsForm.getLastName(), event, false);

ticketReservationRepository.resetVat(reservationId);
ticketReservationRepository.resetVat(reservationId, event.getVatStatus());
if(contactAndTicketsForm.isBusiness()) {
checkAndApplyVATRules(eventName, reservationId, contactAndTicketsForm, bindingResult, event);
}
Expand Down Expand Up @@ -526,7 +526,7 @@ public String handleReservation(@PathVariable("eventName") String eventName,
return redirectReservation(optionalReservation, eventName, reservationId);
}
if (paymentForm.shouldCancelReservation()) {
ticketReservationManager.cancelPendingReservation(reservationId, false);
ticketReservationManager.cancelPendingReservation(reservationId, false, null);
SessionUtil.cleanupSession(request);
return "redirect:/event/" + eventName + "/";
}
Expand Down Expand Up @@ -685,7 +685,7 @@ private Optional<String> checkReservation(boolean backFromOverview, boolean canc
}

if (cancelReservation) {
ticketReservationManager.cancelPendingReservation(reservationId, false); //FIXME
ticketReservationManager.cancelPendingReservation(reservationId, false, null); //FIXME
SessionUtil.cleanupSession(request);
return Optional.of("redirect:/event/" + eventName + "/");
}
Expand Down
Loading

0 comments on commit 7ffd466

Please sign in to comment.