Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(web): add purchase for account from account detail page #208

Merged
merged 1 commit into from
Feb 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

[Compare the full difference.](https://github.com/SFTtech/abrechnung/compare/v0.13.2...HEAD)

- web: add button to add a new purchase for an account from the account detail page

## 0.13.1 (2024-02-11)

[Compare the full difference.](https://github.com/SFTtech/abrechnung/compare/v0.13.1...v0.13.2)
Expand Down
112 changes: 80 additions & 32 deletions frontend/apps/web/src/components/accounts/AccountTransactionList.tsx
Original file line number Diff line number Diff line change
@@ -1,60 +1,108 @@
import { selectClearingAccountsInvolvingAccounts, selectTransactionsInvolvingAccount } from "@abrechnung/redux";
import {
createTransaction,
selectClearingAccountsInvolvingAccounts,
selectTransactionsInvolvingAccount,
} from "@abrechnung/redux";
import { Add as AddIcon } from "@mui/icons-material";
import { Account, Transaction } from "@abrechnung/types";
import { Alert, List } from "@mui/material";
import { Alert, Box, IconButton, List, Tooltip, Typography } from "@mui/material";
import { DateTime } from "luxon";
import * as React from "react";
import { selectAccountSlice, selectTransactionSlice, useAppSelector } from "@/store";
import { selectAccountSlice, selectTransactionSlice, useAppDispatch, useAppSelector } from "@/store";
import { AccountClearingListEntry } from "./AccountClearingListEntry";
import { AccountTransactionListEntry } from "./AccountTransactionListEntry";
import { useTranslation } from "react-i18next";
import { toast } from "react-toastify";
import { useNavigate } from "react-router-dom";
import { PurchaseIcon } from "../style/AbrechnungIcons";

type ArrayAccountsAndTransactions = Array<Transaction | Account>;

interface Props {
groupId: number;
accountId: number;
account: Account;
}

export const AccountTransactionList: React.FC<Props> = ({ groupId, accountId }) => {
export const AccountTransactionList: React.FC<Props> = ({ groupId, account }) => {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const navigate = useNavigate();
const transactions = useAppSelector((state) =>
selectTransactionsInvolvingAccount({ state: selectTransactionSlice(state), groupId, accountId })
selectTransactionsInvolvingAccount({ state: selectTransactionSlice(state), groupId, accountId: account.id })
);
const clearingAccounts = useAppSelector((state) =>
selectClearingAccountsInvolvingAccounts({ state: selectAccountSlice(state), groupId, accountId })
selectClearingAccountsInvolvingAccounts({ state: selectAccountSlice(state), groupId, accountId: account.id })
);

const combinedList: ArrayAccountsAndTransactions = (transactions as ArrayAccountsAndTransactions)
.concat(clearingAccounts)
.sort((f1, f2) => DateTime.fromISO(f2.last_changed).toMillis() - DateTime.fromISO(f1.last_changed).toMillis());

const createTransactionForAccount = () => {
dispatch(
createTransaction({
groupId,
type: "purchase",
data: {
debitor_shares: {
[account.id]: 1,
},
},
})
)
.unwrap()
.then(({ transaction }) => {
navigate(`/groups/${groupId}/transactions/${transaction.id}?no-redirect=true`);
})
.catch(() => toast.error("Creating a transaction failed"));
};

return (
<List>
{combinedList.length === 0 && <Alert severity="info">None so far.</Alert>}
{combinedList.map((entry) => {
if (entry.type === "clearing") {
<>
<Box sx={{ display: "grid", gridTemplateColumns: "auto min-content", justifyContent: "space-between" }}>
<Typography variant="h6">{t("accounts.transactionsInvolving", "", { account })}</Typography>
<Tooltip
title={t("transactions.createPurchaseForAccount", "", {
accountName: account.name,
})}
>
<Box sx={{ display: "flex", flexDirection: "row", alignItems: "center" }}>
<AddIcon color="primary" />
<IconButton color="primary" onClick={createTransactionForAccount}>
<PurchaseIcon />
</IconButton>
</Box>
</Tooltip>
</Box>
<List>
{combinedList.length === 0 && <Alert severity="info">None so far.</Alert>}
{combinedList.map((entry) => {
if (entry.type === "clearing") {
return (
<AccountClearingListEntry
key={`clearing-${entry.id}`}
accountId={account.id}
groupId={groupId}
clearingAccountId={entry.id}
/>
);
}
if (entry.type === "personal") {
return null;
}

// we need to case "entry" to Transaction as typescript cant deduce that it
// has to be a transaction even though we handled all other "type" cases before
return (
<AccountClearingListEntry
key={`clearing-${entry.id}`}
accountId={accountId}
<AccountTransactionListEntry
key={`transaction-${entry.id}`}
accountId={account.id}
groupId={groupId}
clearingAccountId={entry.id}
transactionId={entry.id}
/>
);
}
if (entry.type === "personal") {
return null;
}

// we need to case "entry" to Transaction as typescript cant deduce that it
// has to be a transaction even though we handled all other "type" cases before
return (
<AccountTransactionListEntry
key={`transaction-${entry.id}`}
accountId={accountId}
groupId={groupId}
transactionId={entry.id}
/>
);
})}
</List>
})}
</List>
</>
);
};
47 changes: 25 additions & 22 deletions frontend/apps/web/src/components/accounts/ClearingAccountDetail.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { selectAccountBalances, selectAccountById, selectGroupCurrencySymbol } from "@abrechnung/redux";
import { TableCell } from "@mui/material";
import { TableCell, Typography } from "@mui/material";
import React from "react";
import { selectAccountSlice, selectGroupSlice, useAppSelector } from "@/store";
import { ShareSelect } from "../ShareSelect";
Expand All @@ -26,26 +26,29 @@ export const ClearingAccountDetail: React.FC<Props> = ({ groupId, accountId }) =
}

return (
<ShareSelect
groupId={groupId}
label={t("accounts.participated")}
value={account.clearing_shares}
additionalShareInfoHeader={
<TableCell width="100px" align="right">
{t("common.shared")}
</TableCell>
}
excludeAccounts={[account.id]}
renderAdditionalShareInfo={({ account: participatingAccount }) => (
<TableCell width="100px" align="right">
{formatCurrency(
balances[account.id]?.clearingResolution[participatingAccount.id] ?? 0,
currency_symbol
)}
</TableCell>
)}
onChange={(value) => undefined}
editable={false}
/>
<>
<Typography variant="h6">{t("accounts.clearingDistributionOf", "", { account })}</Typography>
<ShareSelect
groupId={groupId}
label={t("accounts.participated")}
value={account.clearing_shares}
additionalShareInfoHeader={
<TableCell width="100px" align="right">
{t("common.shared")}
</TableCell>
}
excludeAccounts={[account.id]}
renderAdditionalShareInfo={({ account: participatingAccount }) => (
<TableCell width="100px" align="right">
{formatCurrency(
balances[account.id]?.clearingResolution[participatingAccount.id] ?? 0,
currency_symbol
)}
</TableCell>
)}
onChange={(value) => undefined}
editable={false}
/>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -76,15 +76,13 @@ export const AccountDetail: React.FC<Props> = ({ groupId }) => {
{account.type === "clearing" && (
<Grid item xs={12}>
<MobilePaper>
<Typography variant="h6">{t("accounts.clearingDistributionOf", "", { account })}</Typography>
<ClearingAccountDetail groupId={groupId} accountId={accountId} />
</MobilePaper>
</Grid>
)}
<Grid item xs={12}>
<MobilePaper>
<Typography variant="h6">{t("accounts.transactionsInvolving", "", { account })}</Typography>
<AccountTransactionList groupId={groupId} accountId={accountId} />
<AccountTransactionList groupId={groupId} account={account} />
</MobilePaper>
</Grid>
</Grid>
Expand Down
1 change: 1 addition & 0 deletions frontend/libs/translations/src/lib/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ const translations = {
transactions: {
createTransaction: "Create Transaction",
createPurchase: "Create Purchase",
createPurchaseForAccount: "Create purchase with {{accountName}} as participant",
createTransfer: "Create Transfer",
noTransactions: "No Transactions",
purchase: "Purchase",
Expand Down
Loading