mirror of
https://github.com/mvvasilev/personal-finances.git
synced 2025-04-18 21:59:52 +03:00
Fix to and from dates for statistics
This commit is contained in:
parent
51465ad44d
commit
a95fe9dcf4
5 changed files with 42 additions and 20 deletions
|
@ -7,6 +7,7 @@ import dev.mvvasilev.finances.dtos.SpendingOverTimeByCategoryDTO;
|
|||
import dev.mvvasilev.finances.enums.TimePeriod;
|
||||
import dev.mvvasilev.finances.services.StatisticsService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
@ -38,8 +39,10 @@ public class StatisticsController extends AbstractRestController {
|
|||
@PreAuthorize("@authService.isOwner(#categoryId, T(dev.mvvasilev.finances.entity.TransactionCategory))")
|
||||
public ResponseEntity<APIResponseDTO<SpendingByCategoriesDTO>> fetchSpendingByCategory(
|
||||
Long[] categoryId,
|
||||
@RequestParam(defaultValue = "1970-01-01T00:00:00") LocalDateTime from,
|
||||
@RequestParam(defaultValue = "2099-01-01T00:00:00") LocalDateTime to,
|
||||
@RequestParam(defaultValue = "1970-01-01T00:00:00Z")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime from,
|
||||
@RequestParam(defaultValue = "2099-01-01T00:00:00Z")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime to,
|
||||
@RequestParam(defaultValue = "false") Boolean includeUncategorized
|
||||
) {
|
||||
return ok(statisticsService.spendingByCategory(categoryId, from, to));
|
||||
|
@ -50,8 +53,10 @@ public class StatisticsController extends AbstractRestController {
|
|||
public ResponseEntity<APIResponseDTO<SpendingOverTimeByCategoryDTO>> fetchSpendingOverTimeByCategory(
|
||||
Long[] categoryId,
|
||||
@RequestParam(defaultValue = "DAILY") TimePeriod period,
|
||||
@RequestParam(defaultValue = "1970-01-01T00:00:00") LocalDateTime from,
|
||||
@RequestParam(defaultValue = "2099-01-01T00:00:00") LocalDateTime to,
|
||||
@RequestParam(defaultValue = "1970-01-01T00:00:00Z")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime from,
|
||||
@RequestParam(defaultValue = "2099-01-01T00:00:00Z")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime to,
|
||||
@RequestParam(defaultValue = "false") Boolean includeUncategorized
|
||||
) {
|
||||
return ok(statisticsService.spendingByCategoryOverTime(categoryId, period, from, to));
|
||||
|
@ -61,8 +66,10 @@ public class StatisticsController extends AbstractRestController {
|
|||
@PreAuthorize("@authService.isOwner(#categoryId, T(dev.mvvasilev.finances.entity.TransactionCategory))")
|
||||
public ResponseEntity<APIResponseDTO<Double>> sum(
|
||||
Long[] categoryId,
|
||||
@RequestParam(defaultValue = "1970-01-01T00:00:00") LocalDateTime from,
|
||||
@RequestParam(defaultValue = "2099-01-01T00:00:00") LocalDateTime to,
|
||||
@RequestParam(defaultValue = "1970-01-01T00:00:00Z")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime from,
|
||||
@RequestParam(defaultValue = "2099-01-01T00:00:00Z")
|
||||
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime to,
|
||||
@RequestParam(defaultValue = "false") Boolean includeUncategorized
|
||||
) {
|
||||
return ok(statisticsService.sumByCategory(categoryId, from, to, includeUncategorized));
|
||||
|
|
|
@ -74,10 +74,14 @@ public class StatisticsRepository {
|
|||
public Double sumByCategory(Long[] categoryId, LocalDateTime from, LocalDateTime to, Boolean includeUncategorized) {
|
||||
Query nativeQuery = entityManager.createNativeQuery(
|
||||
"""
|
||||
SELECT SUM(pt.amount) AS result
|
||||
FROM transactions.processed_transaction AS pt
|
||||
LEFT OUTER JOIN categories.processed_transaction_category AS ptc ON pt.id = ptc.processed_transaction_id
|
||||
WHERE (ptc.category_id = any(?1) OR (?4 AND ptc.category_id IS NULL)) AND (pt.timestamp BETWEEN ?2 AND ?3)
|
||||
WITH transactions AS (
|
||||
SELECT DISTINCT pt.*
|
||||
FROM transactions.processed_transaction AS pt
|
||||
LEFT OUTER JOIN categories.processed_transaction_category AS ptc ON pt.id = ptc.processed_transaction_id
|
||||
WHERE (pt.timestamp BETWEEN ?2 AND ?3) AND (ptc.category_id = any(?1) OR (?4 AND ptc.category_id IS NULL))
|
||||
)
|
||||
SELECT COALESCE(SUM(pt.amount), 0) AS result
|
||||
FROM transactions AS pt
|
||||
""",
|
||||
Tuple.class
|
||||
);
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
} from '@mui/material';
|
||||
import Grid from "@mui/material/Unstable_Grid2";
|
||||
import Button from "@mui/material/Button";
|
||||
import {Addchart, Save} from "@mui/icons-material";
|
||||
import {Addchart, Save, Warning} from "@mui/icons-material";
|
||||
import WidgetContainer from "@/components/widgets/WidgetContainer.jsx";
|
||||
import 'react-grid-layout/css/styles.css';
|
||||
import {useEffect, useState} from "react";
|
||||
|
@ -25,6 +25,7 @@ export default function StatisticsPage() {
|
|||
const [isWidgetModalOpen, openWidgetModal] = useState(false);
|
||||
const [isRemoveWidgetDialogShown, showRemoveWidgetDialog] = useState(false);
|
||||
const [removingWidgetId, setRemovingWidgetId] = useState(null);
|
||||
const [isUnsavedLayoutWarningShown, showUnsavedLayoutWarning] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchWidgets();
|
||||
|
@ -48,7 +49,10 @@ export default function StatisticsPage() {
|
|||
parameters: w.parameters
|
||||
}
|
||||
}) ?? []))
|
||||
.then(r => utils.hideSpinner());
|
||||
.then(r => {
|
||||
utils.hideSpinner();
|
||||
showUnsavedLayoutWarning(false);
|
||||
});
|
||||
}
|
||||
|
||||
function openWidgetCreationModal() {
|
||||
|
@ -119,8 +123,11 @@ export default function StatisticsPage() {
|
|||
|
||||
toast.promise(
|
||||
Promise.resolve(promises)
|
||||
.then(r => fetchWidgets())
|
||||
.then(r => utils.hideSpinner()),
|
||||
//.then(r => fetchWidgets())
|
||||
.then(r => {
|
||||
utils.hideSpinner();
|
||||
showUnsavedLayoutWarning(false);
|
||||
}),
|
||||
{
|
||||
loading: "Saving...",
|
||||
success: "Saved",
|
||||
|
@ -202,7 +209,7 @@ export default function StatisticsPage() {
|
|||
sx={{
|
||||
width: "100%"
|
||||
}}
|
||||
variant="contained"
|
||||
variant={isUnsavedLayoutWarningShown ? "contained" : "outlined"}
|
||||
onClick={saveWidgetsLayout}
|
||||
startIcon={<Save/>}
|
||||
>
|
||||
|
@ -225,6 +232,7 @@ export default function StatisticsPage() {
|
|||
})
|
||||
|
||||
setWidgets([...newWidgets])
|
||||
console.log("layout change")
|
||||
}}
|
||||
draggableCancel=".grid-drag-cancel"
|
||||
cols={{lg: 12, md: 10, sm: 6, xs: 4, xxs: 2}}
|
||||
|
|
|
@ -41,8 +41,8 @@ export default function WidgetContainer({widget, sx, onEdit, onRemove}) {
|
|||
|
||||
var includeUncategorized = widget.parameters?.find(p => p.name === PARAMS.INCLUDE_UNCATEGORIZED)?.booleanValue ?? false;
|
||||
|
||||
queryString += `&fromDate=${fromDate}`;
|
||||
queryString += `&toDate=${toDate}`;
|
||||
queryString += `&from=${fromDate.toISOString()}`;
|
||||
queryString += `&to=${toDate.toISOString()}`;
|
||||
queryString += `&includeUncategorized=${includeUncategorized}`;
|
||||
|
||||
switch (widget.type) {
|
||||
|
@ -118,7 +118,7 @@ export default function WidgetContainer({widget, sx, onEdit, onRemove}) {
|
|||
<Grid xs={12} lg={12}>
|
||||
<div className={"grid-drag-cancel"} style={{ position: "relative", height: "100%", width: "100%" }}>
|
||||
{
|
||||
data && widget.type === "TOTAL_SPENDING_PER_CATEGORY" &&
|
||||
!utils.isNullOrUndefined(data) && widget.type === "TOTAL_SPENDING_PER_CATEGORY" &&
|
||||
<Pie
|
||||
options={{
|
||||
responsive: true,
|
||||
|
@ -137,11 +137,11 @@ export default function WidgetContainer({widget, sx, onEdit, onRemove}) {
|
|||
/>
|
||||
}
|
||||
{
|
||||
data && widget.type === "SPENDING_OVER_TIME_PER_CATEGORY" &&
|
||||
!utils.isNullOrUndefined(data) && widget.type === "SPENDING_OVER_TIME_PER_CATEGORY" &&
|
||||
<Line />
|
||||
}
|
||||
{
|
||||
data && widget.type === "SUM_PER_CATEGORY" &&
|
||||
!utils.isNullOrUndefined(data) && widget.type === "SUM_PER_CATEGORY" &&
|
||||
<Typography sx={{
|
||||
fontSize: "2.3em"
|
||||
}}>
|
||||
|
|
|
@ -41,6 +41,9 @@ let utils = {
|
|||
},
|
||||
formatCurrency(number) {
|
||||
return LEV_FORMAT.format(number);
|
||||
},
|
||||
isNullOrUndefined(obj) {
|
||||
return obj === null || obj === undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue