Various stuff all over the tree

- Increase Java version to 1.9
- Add commons-collection and Jollyday dependencies
- Add JavaDoc plugin
- Add country and state configuration for Jollyday library
- Add WIP implementation of the recurring transaction feature
- Improve JavaDoc
- Use Java 8 date API
- Reformatting
- Add special Flyway migration version for test data
- Add and improve unit tests
This commit is contained in:
2019-03-01 20:39:31 +01:00
parent fe5380a865
commit 35902afe43
21 changed files with 950 additions and 102 deletions

View File

@@ -0,0 +1,53 @@
package de.financer.config;
import de.jollyday.HolidayCalendar;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.Optional;
@Configuration
@ConfigurationProperties(prefix = "financer")
public class FinancerConfig {
private String countryCode;
private String state;
/**
* @return the raw country code, mostly an uppercase ISO 3166 2-letter code
*/
public String getCountryCode() {
return countryCode;
}
/**
* @return the state
*/
public String getState() {
return state;
}
/**
* @return the {@link HolidayCalendar} used to calculate the holidays. Internally uses the country code
* specified via {@link FinancerConfig#getCountryCode}.
*/
public HolidayCalendar getHolidayCalendar() {
final Optional<HolidayCalendar> optionalHoliday = Arrays.asList(HolidayCalendar.values()).stream()
.filter((hc) -> hc.getId().equals(this.countryCode))
.findFirst();
if (!optionalHoliday.isPresent()) {
// TODO log info about default DE
}
return optionalHoliday.orElse(HolidayCalendar.GERMANY);
}
public void setState(String state) {
this.state = state;
}
public void setCountryCode(String countryCode) {
this.countryCode = countryCode;
}
}

View File

@@ -0,0 +1,43 @@
package de.financer.controller;
import de.financer.model.RecurringTransaction;
import de.financer.service.RecurringTransactionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("recurringTransactions")
public class RecurringTransactionController {
@Autowired
private RecurringTransactionService recurringTransactionService;
@RequestMapping("getAll")
public Iterable<RecurringTransaction> getAll() {
return this.recurringTransactionService.getAll();
}
@RequestMapping("getAllForAccount")
public Iterable<RecurringTransaction> getAllForAccount(String accountKey) {
return this.recurringTransactionService.getAllForAccount(accountKey);
}
@RequestMapping("getAllDueToday")
public Iterable<RecurringTransaction> getAllDueToday() {
return this.recurringTransactionService.getAllDueToday();
}
@RequestMapping("createRecurringTransaction")
public ResponseEntity createRecurringTransaction(String fromAccountKey, String toAccountKey, Long amount,
String description, String holidayWeekendType,
String intervalType, String firstOccurrence,
String lastOccurrence) {
return this.recurringTransactionService.createRecurringTransaction(fromAccountKey, toAccountKey, amount,
description, holidayWeekendType,
intervalType, firstOccurrence,
lastOccurrence)
.toResponseEntity();
}
}

View File

@@ -23,8 +23,10 @@ public class TransactionController {
return this.transactionService.getAllForAccount(accountKey);
}
@RequestMapping("createTransaction")
public ResponseEntity createTransaction(String fromAccountKey, String toAccountKey, Long amount, String date, String description) {
return this.transactionService.createTransaction(fromAccountKey, toAccountKey, amount, date, description).toResponseEntity();
@RequestMapping(value = "createTransaction")
public ResponseEntity createTransaction(String fromAccountKey, String toAccountKey, Long amount, String date,
String description) {
return this.transactionService.createTransaction(fromAccountKey, toAccountKey, amount, date, description)
.toResponseEntity();
}
}

View File

@@ -1,7 +1,12 @@
package de.financer.dba;
import de.financer.model.Account;
import de.financer.model.RecurringTransaction;
import org.springframework.data.repository.CrudRepository;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
@Transactional(propagation = Propagation.REQUIRED)
public interface RecurringTransactionRepository extends CrudRepository<RecurringTransaction, Long> {
Iterable<RecurringTransaction> findRecurringTransactionsByFromAccountOrToAccount(Account fromAccount, Account toAccount);
}

View File

@@ -10,10 +10,47 @@ public enum HolidayWeekendType {
/** Indicates that the action should be done on the specified day regardless whether it's a holiday or a weekend */
SAME_DAY,
/** Indicates that the action should be deferred to the next workday */
/**
* <p>
* Indicates that the action should be deferred to the next workday.
* </p>
* <pre>
* Example 1:
* MO TU WE TH FR SA SO
* H WE WE -&gt; Holiday/WeekEnd
* X -&gt; Due date of action
* X' -&gt; Deferred, effective due date of action
* </pre>
* <pre>
* Example 2:
* TU WE TH FR SA SO MO
* H WE WE -&gt; Holiday/WeekEnd
* X -&gt; Due date of action
* X' -&gt; Deferred, effective due date of action
* </pre>
*
*/
NEXT_WORKDAY,
/** Indicates that the action should be dated back to the previous day */
/**
* <p>
* Indicates that the action should be made earlier at the previous day
* </p>
* <pre>
* Example 1:
* MO TU WE TH FR SA SO
* H WE WE -&gt; Holiday/WeekEnd
* X -&gt; Due date of action
* X' -&gt; Earlier, effective due date of action
* </pre>
* <pre>
* Example 2:
* MO TU WE TH FR SA SO
* H WE WE -&gt; Holiday/WeekEnd
* X -&gt; Due date of action
* X' -&gt; Earlier, effective due date of action
* </pre>
*/
PREVIOUS_WORKDAY;
/**

View File

@@ -1,7 +1,7 @@
package de.financer.model;
import javax.persistence.*;
import java.util.Date;
import java.time.LocalDate;
@Entity
public class RecurringTransaction {
@@ -16,10 +16,8 @@ public class RecurringTransaction {
private Long amount;
@Enumerated(EnumType.STRING)
private IntervalType intervalType;
@Temporal(TemporalType.DATE)
private Date firstOccurrence;
@Temporal(TemporalType.DATE)
private Date lastOccurrence;
private LocalDate firstOccurrence;
private LocalDate lastOccurrence;
@Enumerated(EnumType.STRING)
private HolidayWeekendType holidayWeekendType;
@@ -67,19 +65,19 @@ public class RecurringTransaction {
this.holidayWeekendType = holidayWeekendType;
}
public Date getLastOccurrence() {
public LocalDate getLastOccurrence() {
return lastOccurrence;
}
public void setLastOccurrence(Date lastOccurrence) {
public void setLastOccurrence(LocalDate lastOccurrence) {
this.lastOccurrence = lastOccurrence;
}
public Date getFirstOccurrence() {
public LocalDate getFirstOccurrence() {
return firstOccurrence;
}
public void setFirstOccurrence(Date firstOccurrence) {
public void setFirstOccurrence(LocalDate firstOccurrence) {
this.firstOccurrence = firstOccurrence;
}

View File

@@ -1,7 +1,7 @@
package de.financer.model;
import javax.persistence.*;
import java.util.Date;
import java.time.LocalDate;
@Entity
@Table(name = "\"transaction\"")
@@ -13,9 +13,8 @@ public class Transaction {
private Account fromAccount;
@OneToOne(fetch = FetchType.EAGER)
private Account toAccount;
@Temporal(TemporalType.DATE)
@Column(name = "\"date\"")
private Date date;
private LocalDate date;
private String description;
private Long amount;
@ManyToOne(fetch = FetchType.EAGER)
@@ -41,11 +40,11 @@ public class Transaction {
this.toAccount = toAccount;
}
public Date getDate() {
public LocalDate getDate() {
return date;
}
public void setDate(Date date) {
public void setDate(LocalDate date) {
this.date = date;
}

View File

@@ -0,0 +1,10 @@
/**
* <p>
* This package contains the main model for the financer application.
* In the DDD (<i>Domain Driven Design</i>) sense the models are anemic
* as they contain no logic themselves but act as mere POJOs with additional
* Hibernate annotations. The (business) logic is located in the services of the
* {@link de.financer.service} package.
* </p>
*/
package de.financer.model;

View File

@@ -0,0 +1,163 @@
package de.financer.service;
import de.financer.ResponseReason;
import de.financer.dba.RecurringTransactionRepository;
import de.financer.model.Account;
import de.financer.model.HolidayWeekendType;
import de.financer.model.RecurringTransaction;
import org.apache.commons.collections4.IterableUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.LocalDate;
import java.util.Collections;
import java.util.stream.Collectors;
@Service
public class RecurringTransactionService {
@Autowired
private RecurringTransactionRepository recurringTransactionRepository;
@Autowired
private AccountService accountService;
@Autowired
private RuleService ruleService;
public Iterable<RecurringTransaction> getAll() {
return this.recurringTransactionRepository.findAll();
}
public Iterable<RecurringTransaction> getAllForAccount(String accountKey) {
final Account account = this.accountService.getAccountByKey(accountKey);
if (account == null) {
return Collections.emptyList();
}
// As we want all transactions of the given account use it as from and to account
return this.recurringTransactionRepository.findRecurringTransactionsByFromAccountOrToAccount(account, account);
}
/**
* This method gets all recurring transactions that are due today. Whether a recurring transaction is due today
* depends on today's date and the configured {@link RecurringTransaction#getIntervalType() interval type}
* and {@link RecurringTransaction#getHolidayWeekendType() holiday weekend type}.
*
* @return all recurring transactions that are due today
*/
public Iterable<RecurringTransaction> getAllDueToday() {
return this.getAllDueToday(LocalDate.now());
}
// Visible for unit tests
/* package */ Iterable<RecurringTransaction> getAllDueToday(LocalDate now) {
// TODO filter for lastOccurrence not in the past
final Iterable<RecurringTransaction> allRecurringTransactions = this.recurringTransactionRepository.findAll();
//@formatter:off
return IterableUtils.toList(allRecurringTransactions).stream()
.filter((rt) -> checkRecurringTransactionDueToday(rt, now) ||
checkRecurringTransactionDuePast(rt, now))
// TODO checkRecurringTransactionDueFuture for HolidayWeekendType.PREVIOUS_WORKDAY
.collect(Collectors.toList());
//@formatter:on
}
/**
* This method checks whether the given {@link RecurringTransaction} is due today.
* A recurring transaction is due if the current {@link LocalDate date} is a multiple of the
* {@link RecurringTransaction#getFirstOccurrence() first occurrence} of the recurring transaction and the
* {@link RecurringTransaction#getIntervalType() interval type}. If today is a
* {@link RuleService#isHoliday(LocalDate) holiday} or a
* {@link RuleService#isWeekend(LocalDate) weekend day} the
* {@link HolidayWeekendType holiday weekend type}
* is taken into account to decide whether the recurring transaction should be deferred.
*
* @param recurringTransaction to check whether it is due today
* @param now today's date
* @return <code>true</code> if the recurring transaction is due today, <code>false</code> otherwise
*/
private boolean checkRecurringTransactionDueToday(RecurringTransaction recurringTransaction, LocalDate now) {
final boolean holiday = this.ruleService.isHoliday(now);
final boolean dueToday = recurringTransaction.getFirstOccurrence()
// This calculates all dates between the first occurrence of the
// recurring transaction and tomorrow for the interval specified
// by the recurring transaction. We need to use tomorrow as
// upper bound of the interval because the upper bound is exclusive
// in the datesUntil method.
.datesUntil(now.plusDays(1), this.ruleService
.getPeriodForInterval(recurringTransaction
.getIntervalType()))
// Then we check whether today is a date in the calculated range.
// If so the recurring transaction is due today
.anyMatch((d) -> d.equals(now));
final boolean weekend = this.ruleService.isWeekend(now);
boolean defer = false;
if (holiday || weekend) {
defer = recurringTransaction.getHolidayWeekendType() == HolidayWeekendType.NEXT_WORKDAY;
}
return !defer && dueToday;
}
/**
* This method checks whether the given {@link RecurringTransaction} was actually due in the close past
* but has been deferred to <i>maybe</i> today because the actual due day has been a holiday or weekend day and the
* {@link RecurringTransaction#getHolidayWeekendType() holiday weekend type} was
* {@link HolidayWeekendType#NEXT_WORKDAY}. Note that the recurring transaction may get deferred again if today
* again is a holiday or a weekend day.
* The period this method considers starts with today and ends with the last workday (no
* {@link RuleService#isHoliday(LocalDate) holiday}, not a {@link RuleService#isWeekend(LocalDate) weekend day})
* whereas the end is exclusive, because if the recurring transaction would have been due at the last workday day
* it wouldn't has been deferred.
*
* @param recurringTransaction to check whether it is due today
* @param now today's date
* @return <code>true</code> if the recurring transaction is due today, <code>false</code> otherwise
*/
private boolean checkRecurringTransactionDuePast(RecurringTransaction recurringTransaction, LocalDate now) {
boolean weekend;
boolean holiday;
LocalDate yesterday = now;
boolean due = false;
// Go back in time until we hit the first non-holiday, non-weekend day
// and check for every day in between if the given recurring transaction was due on this day
do {
yesterday = yesterday.minusDays(1);
holiday = this.ruleService.isHoliday(yesterday);
weekend = this.ruleService.isWeekend(yesterday);
if (holiday || weekend) {
// Lambdas require final local variables
final LocalDate finalYesterday = yesterday;
// For an explanation of the expression see the ...DueToday method
due = recurringTransaction.getFirstOccurrence()
.datesUntil(yesterday.plusDays(1), this.ruleService
.getPeriodForInterval(recurringTransaction
.getIntervalType()))
.anyMatch((d) -> d.equals(finalYesterday));
if (due) {
break;
}
}
}
while (holiday || weekend);
return due;
}
public ResponseReason createRecurringTransaction(String fromAccountKey, String toAccountKey, Long amount,
String description, String holidayWeekendType,
String intervalType, String firstOccurrence,
String lastOccurrence) {
return null;
}
}

View File

@@ -1,27 +1,51 @@
package de.financer.service;
import de.financer.config.FinancerConfig;
import de.financer.model.Account;
import de.financer.model.AccountType;
import de.financer.model.IntervalType;
import de.jollyday.HolidayManager;
import de.jollyday.ManagerParameters;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.Period;
import java.util.*;
import static de.financer.model.AccountType.*;
/**
* This service encapsulates methods that form business logic rules.
* This service encapsulates methods that form basic logic rules.
* While most of the logic could be placed elsewhere this service provides
* centralized access to these rules.
* centralized access to these rules. Placing them in here also enables easy
* unit testing.
*/
@Service
public class RuleService implements InitializingBean {
@Autowired
private FinancerConfig financerConfig;
private Map<AccountType, Collection<AccountType>> bookingRules;
private Map<IntervalType, Period> intervalPeriods;
@Override
public void afterPropertiesSet() {
initBookingRules();
initIntervalValues();
}
private void initIntervalValues() {
this.intervalPeriods = new EnumMap<>(IntervalType.class);
this.intervalPeriods.put(IntervalType.DAILY, Period.ofDays(1));
this.intervalPeriods.put(IntervalType.WEEKLY, Period.ofWeeks(1));
this.intervalPeriods.put(IntervalType.MONTHLY, Period.ofMonths(1));
this.intervalPeriods.put(IntervalType.QUARTERLY, Period.ofMonths(3));
this.intervalPeriods.put(IntervalType.YEARLY, Period.ofYears(1));
}
private void initBookingRules() {
@@ -40,7 +64,7 @@ public class RuleService implements InitializingBean {
/**
* This method returns the multiplier for the given from account.
*
* <p>
* The multiplier controls whether the current amount of the given from account is increased or
* decreased depending on the {@link AccountType} of the given account.
*
@@ -55,17 +79,13 @@ public class RuleService implements InitializingBean {
if (INCOME.equals(accountType)) {
return 1L;
}
else if (BANK.equals(accountType)) {
} else if (BANK.equals(accountType)) {
return -1L;
}
else if (CASH.equals(accountType)) {
} else if (CASH.equals(accountType)) {
return -1L;
}
else if (LIABILITY.equals(accountType)) {
} else if (LIABILITY.equals(accountType)) {
return 1L;
}
else if (START.equals(accountType)) {
} else if (START.equals(accountType)) {
return 1L;
}
@@ -74,7 +94,7 @@ public class RuleService implements InitializingBean {
/**
* This method returns the multiplier for the given to account.
*
* <p>
* The multiplier controls whether the current amount of the given to account is increased or
* decreased depending on the {@link AccountType} of the given account.
*
@@ -89,14 +109,11 @@ public class RuleService implements InitializingBean {
if (BANK.equals(accountType)) {
return 1L;
}
else if (CASH.equals(accountType)) {
} else if (CASH.equals(accountType)) {
return 1L;
}
else if (LIABILITY.equals(accountType)) {
} else if (LIABILITY.equals(accountType)) {
return -1L;
}
else if (EXPENSE.equals(accountType)) {
} else if (EXPENSE.equals(accountType)) {
return 1L;
}
@@ -109,10 +126,43 @@ public class RuleService implements InitializingBean {
* account does not make sense and is declared as invalid.
*
* @param fromAccount the account to subtract the money from
* @param toAccount the account to add the money to
* @return <code>true</code> if the from->to relationship of the given accounts is valid, <code>false</code> otherwise
* @param toAccount the account to add the money to
* @return <code>true</code> if the from-&gt;to relationship of the given accounts is valid, <code>false</code> otherwise
*/
public boolean isValidBooking(Account fromAccount, Account toAccount) {
return this.bookingRules.get(fromAccount.getType()).contains(toAccount.getType());
}
/**
* This method gets the {@link Period} for the given {@link IntervalType},
* e.g. a period of three months for {@link IntervalType#QUARTERLY}.
*
* @param intervalType to get the period for
* @return the period matching the interval type
*/
public Period getPeriodForInterval(IntervalType intervalType) {
return this.intervalPeriods.get(intervalType);
}
/**
* This method checks whether the given date is a holiday in the configured country and state.
*
* @param now the date to check
* @return <code>true</code> if the given date is a holiday, <code>false</code> otherwise
*/
public boolean isHoliday(LocalDate now) {
return HolidayManager.getInstance(ManagerParameters.create(this.financerConfig.getHolidayCalendar()))
.isHoliday(now, this.financerConfig.getState());
}
/**
* This method checks whether the given date is a weekend day, i.e. whether it's a
* {@link DayOfWeek#SATURDAY} or {@link DayOfWeek#SUNDAY}.
*
* @param now the date to check
* @return <code>true</code> if the given date is a weekend day, <code>false</code> otherwise
*/
public boolean isWeekend(LocalDate now) {
return EnumSet.of(DayOfWeek.SATURDAY, DayOfWeek.SUNDAY).contains(now.getDayOfWeek());
}
}

View File

@@ -9,8 +9,9 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Collections;
@Service
@@ -36,7 +37,7 @@ public class TransactionService {
/**
* @param accountKey the key of the account to get the transactions for
* @return all transactions for the given account, for all time. Returns an empty list if the given key does not
* match any account.
* match any account.
*/
public Iterable<Transaction> getAllForAccount(String accountKey) {
final Account account = this.accountService.getAccountByKey(accountKey);
@@ -63,8 +64,10 @@ public class TransactionService {
try {
final Transaction transaction = buildTransaction(fromAccount, toAccount, amount, description, date);
fromAccount.setCurrentBalance(fromAccount.getCurrentBalance() + (this.ruleService.getMultiplierFromAccount(fromAccount) * amount));
toAccount.setCurrentBalance(toAccount.getCurrentBalance() + (this.ruleService.getMultiplierToAccount(toAccount) * amount));
fromAccount.setCurrentBalance(fromAccount.getCurrentBalance() + (this.ruleService
.getMultiplierFromAccount(fromAccount) * amount));
toAccount.setCurrentBalance(toAccount.getCurrentBalance() + (this.ruleService
.getMultiplierToAccount(toAccount) * amount));
this.transactionRepository.save(transaction);
@@ -72,13 +75,11 @@ public class TransactionService {
this.accountService.saveAccount(toAccount);
response = ResponseReason.OK;
}
catch(ParseException e) {
} catch (DateTimeParseException e) {
// TODO log
response = ResponseReason.INVALID_DATE_FORMAT;
}
catch (Exception e) {
} catch (Exception e) {
// TODO log
response = ResponseReason.UNKNOWN_ERROR;
@@ -91,21 +92,21 @@ public class TransactionService {
* This method builds the actual transaction object with the given values.
*
* @param fromAccount the from account
* @param toAccount the to account
* @param amount the transaction amount
* @param toAccount the to account
* @param amount the transaction amount
* @param description the description of the transaction
* @param date the date of the transaction
* @param date the date of the transaction
* @return the build {@link Transaction} instance
* @throws ParseException if the given date string cannot be parsed into a {@link java.util.Date} instance
* @throws DateTimeParseException if the given date string cannot be parsed into a {@link java.time.LocalDate} instance
*/
private Transaction buildTransaction(Account fromAccount, Account toAccount, Long amount, String description, String date) throws ParseException {
private Transaction buildTransaction(Account fromAccount, Account toAccount, Long amount, String description, String date) throws DateTimeParseException {
final Transaction transaction = new Transaction();
transaction.setFromAccount(fromAccount);
transaction.setToAccount(toAccount);
transaction.setAmount(amount);
transaction.setDescription(description);
transaction.setDate(new SimpleDateFormat(DATE_FORMAT).parse(date));
transaction.setDate(LocalDate.parse(date, DateTimeFormatter.ofPattern(DATE_FORMAT)));
return transaction;
}
@@ -114,9 +115,9 @@ public class TransactionService {
* This method checks whether the parameters for creating a transaction are valid.
*
* @param fromAccount the from account
* @param toAccount the to account
* @param amount the transaction amount
* @param date the transaction date
* @param toAccount the to account
* @param amount the transaction amount
* @param date the transaction date
* @return the first error found or <code>null</code> if all parameters are valid
*/
private ResponseReason validateParameters(Account fromAccount, Account toAccount, Long amount, String date) {
@@ -124,23 +125,17 @@ public class TransactionService {
if (fromAccount == null && toAccount == null) {
response = ResponseReason.FROM_AND_TO_ACCOUNT_NOT_FOUND;
}
else if (toAccount == null) {
} else if (toAccount == null) {
response = ResponseReason.TO_ACCOUNT_NOT_FOUND;
}
else if (fromAccount == null) {
} else if (fromAccount == null) {
response = ResponseReason.FROM_ACCOUNT_NOT_FOUND;
}
else if (!this.ruleService.isValidBooking(fromAccount, toAccount)) {
} else if (!this.ruleService.isValidBooking(fromAccount, toAccount)) {
response = ResponseReason.INVALID_BOOKING_ACCOUNTS;
}
else if (amount == null) {
} else if (amount == null) {
response = ResponseReason.MISSING_AMOUNT;
}
else if (amount == 0l) {
} else if (amount == 0l) {
response = ResponseReason.AMOUNT_ZERO;
}
else if (date == null) {
} else if (date == null) {
response = ResponseReason.MISSING_DATE;
}

View File

@@ -0,0 +1,9 @@
/**
* <p>
* This package contains the actual business logic services of the financer application.
* They get called by the {@link de.financer.controller controller} layer. Also they call each other,
* e.g. the {@link de.financer.service.TransactionService} internally uses the {@link de.financer.service.RuleService}.
* Data access is done via the {@link de.financer.dba DBA} layer.
* </p>
*/
package de.financer.service;

View File

@@ -12,4 +12,13 @@ info.app.name=Financer
info.app.description=A simple server for personal finance administration
info.build.group=@project.groupId@
info.build.artifact=@project.artifactId@
info.build.version=@project.version@
info.build.version=@project.version@
# Country code for holiday checks
# Mostly an uppercase ISO 3166 2-letter code
# For a complete list of the supported codes see https://github.com/svendiedrichsen/jollyday/blob/master/src/main/java/de/jollyday/HolidayCalendar.java
financer.countryCode=DE
# The state used for holiday checks
# For a complete list of the supported states see e.g. https://github.com/svendiedrichsen/jollyday/blob/master/src/main/resources/holidays/Holidays_de.xml
financer.state=sl

View File

@@ -1,8 +1,8 @@
--
-- This file contains the basic initialization of the financer schema and init data
-- This file contains the basic initialization of the financer schema
--
-- Account table and init data
-- Account table
CREATE TABLE account (
id BIGINT NOT NULL PRIMARY KEY IDENTITY,
"key" VARCHAR(1000) NOT NULL, --escape keyword "key"
@@ -13,18 +13,6 @@ CREATE TABLE account (
CONSTRAINT un_account_name_key UNIQUE ("key")
);
INSERT INTO account ("key", type, status, current_balance)
VALUES ('accounts.checkaccount', 'BANK', 'OPEN', 0);
INSERT INTO account ("key", type, status, current_balance)
VALUES ('accounts.income', 'INCOME', 'OPEN', 0);
INSERT INTO account ("key", type, status, current_balance)
VALUES ('accounts.cash', 'CASH', 'OPEN', 0);
INSERT INTO account ("key", type, status, current_balance)
VALUES ('accounts.start', 'START', 'OPEN', 0);
-- Recurring transaction table
CREATE TABLE recurring_transaction (
id BIGINT NOT NULL PRIMARY KEY IDENTITY,