diff --git a/src/core/api.jsx b/src/core/api.jsx
index 4ce3dc2..00722ea 100644
--- a/src/core/api.jsx
+++ b/src/core/api.jsx
@@ -88,7 +88,6 @@ const api = () => {
bulkEditTransactions: (payload, callback) => apiCall(PUT, 'transaction/bulk', callback, payload),
deleteTransaction: (id, callback) => apiCall(DELETE, `transaction/${id}`, callback),
suggestRemarks: (input, callback) => apiCall(GET, `suggest/remarks?q=${clean(input)}`, callback),
- suggestCode: (input, callback) => apiCall(GET, `suggest/code?q=${clean(input)}`, callback),
suggestCompany: (input, callback) => apiCall(GET, `suggest/company?q=${clean(input)}`, callback),
getCategories: (callback) => apiCall(GET, 'suggest/categories', callback),
listTemplates: (callback) => apiCall(GET, 'template', callback),
diff --git a/src/settings/cpf-slider.jsx b/src/settings/cpf-slider.jsx
index 5075dd6..d4c99ef 100644
--- a/src/settings/cpf-slider.jsx
+++ b/src/settings/cpf-slider.jsx
@@ -8,7 +8,7 @@ const CpfSlider = ({ cpfRatio, setCpfRatio, cpfAllocationInvalid }) => {
(parseFloat(cpfRatio.ordinaryRatio) + parseFloat(cpfRatio.specialRatio))
];
- const updateCpfSlider = (event, newValue) => setCpfRatio({
+ const updateCpfSlider = (_, newValue) => setCpfRatio({
ordinaryRatio: newValue[0],
specialRatio: parseFloat((newValue[1] - newValue[0]).toFixed(4)),
medisaveRatio: parseFloat((1 - newValue[1]).toFixed(4)),
diff --git a/src/transactions/add-transaction-dialog.jsx b/src/transactions/add-transaction-dialog.jsx
index 65fdc7c..880b810 100644
--- a/src/transactions/add-transaction-dialog.jsx
+++ b/src/transactions/add-transaction-dialog.jsx
@@ -1,5 +1,6 @@
import 'dayjs/locale/en-sg';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
+import { cpfCodes } from '../util/cpf-codes';
import { createFilterOptions } from '@mui/material/Autocomplete';
import { DatePicker } from '@mui/x-date-pickers/DatePicker';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
@@ -60,7 +61,7 @@ const AddTransactionDialog = ({
const setVisibleTransactionId = state.useState(state.visibleTransactionId)[1];
const {
listAccounts, addTransaction, editTransaction, listTransactions,
- showStatus, suggestRemarks, suggestCode, suggestCompany, getCategories,
+ showStatus, suggestRemarks, suggestCompany, getCategories,
} = api();
useEffect(() => {
@@ -100,6 +101,8 @@ const AddTransactionDialog = ({
} else {
setMonth(date.startOf('month'));
}
+ } else if (selectedAccount?.type === 'Retirement') {
+ setMonth(date.subtract(1, 'month').startOf('month'));
}
}, [ date ]);
@@ -125,6 +128,9 @@ const AddTransactionDialog = ({
if (tx.originalAmount) {
tx.originalAmount *= side;
}
+ if (tx.code && tx.code.indexOf(':') > -1) {
+ tx.code = tx.code.split(':').shift();
+ }
tx['@type'] = (selectedAccount?.multiCurrency ? 'fx-' : '') + selectedAccount?.type.toLowerCase();
if (selectedAccount?.type === 'Credit') {
tx.billingMonth = month.toISOString();
@@ -356,7 +362,7 @@ const AddTransactionDialog = ({
options={categories}
filterOptions={createFilterOptions({ limit: 5 })}
value={category}
- onChange={(e, v) => setCategory(v)}
+ onChange={(_, v) => setCategory(v)}
renderInput={(params) => (
),
code: (
- c.code)}
+ filterOptions={createFilterOptions({ limit: 5 })}
+ getOptionLabel={(o) => cpfCodes.find(({ code }) => code === o) ? (o + ': ' + cpfCodes.find(({ code }) => code === o).description) : o}
+ renderInput={(params) => (
+
+ )}
value={code}
- onChange={(e, v) => setCode(v.toUpperCase())}
- fieldProps={{
- required: true,
- inputProps: { minLength: 2 },
- name: 'code',
- label: 'Code'
- }}
+ onChange={(_, v) => setCode(v?.toUpperCase() || '')}
/>
),
company: (
@@ -391,6 +404,7 @@ const AddTransactionDialog = ({
name: 'company',
label: 'Company'
}}
+ sx={{ display: (code === 'CON') ? 'flex' : 'none' }}
/>
),
ordinaryAmount: (
diff --git a/src/transactions/auto-fill.jsx b/src/transactions/auto-fill.jsx
index 16b1323..e4a5b6e 100644
--- a/src/transactions/auto-fill.jsx
+++ b/src/transactions/auto-fill.jsx
@@ -13,7 +13,7 @@ const AutoFill = ({ initValue, fieldProps, promise, ...props }) => {
promise(request, callback), 200), []);
useEffect(() => {
- if (inputValue.length < 3) {
+ if (inputValue.length < 1) {
return;
}
setOptions([]);
diff --git a/src/transactions/transactions-grid.jsx b/src/transactions/transactions-grid.jsx
index 08bda06..de98cd0 100644
--- a/src/transactions/transactions-grid.jsx
+++ b/src/transactions/transactions-grid.jsx
@@ -10,6 +10,7 @@ import {
import {
formatNumber, formatDecimal, formatDecimalHideZero, formatDecimalAbs, formatDate, formatMonth,
} from '../util/formatters';
+import { cpfCodes } from '../util/cpf-codes';
import { GridPagination } from '@mui/x-data-grid';
import { HorizontalLoader } from '../core/loader';
import { pink, lightGreen } from '@mui/material/colors';
@@ -19,6 +20,7 @@ import api from '../core/api';
import Box from '@mui/system/Box';
import state from '../core/state';
import styled from 'styled-components';
+import Tooltip from '@mui/material/Tooltip';
import useMediaQuery from '@mui/material/useMediaQuery';
const GridBox = styled.div`
@@ -102,6 +104,11 @@ const TransactionsGrid = ({ accounts, setShowAddDialog, setTransactionToEdit, ap
const getColourClassForValue = ({ value }) => !value ? '' : value > 0 ? 'green' : 'red';
+ const CpfCode = ({ value }) => {
+ const title = cpfCodes.find((c) => c.code === value)?.description || 'No description available';
+ return {value};
+ };
+
const columns = {
account: { flex : 2, field: 'accountId', headerName: 'Account', valueGetter: getAccountName },
date: { flex: 2.5, field: 'date', headerName: 'Date', type: 'date', valueFormatter: formatDate },
@@ -115,7 +122,7 @@ const TransactionsGrid = ({ accounts, setShowAddDialog, setTransactionToEdit, ap
balance: { flex: 2, field: 'balance', headerName: 'Balance', type: 'number', valueFormatter: formatDecimal },
remarks: { flex: 4, field: 'remarks', headerName: 'Remarks' },
category: { flex: 2, field: 'category', headerName: 'Category' },
- code: { flex: 2, field: 'code', headerName: 'Code' },
+ code: { flex: 2, field: 'code', headerName: 'Code', renderCell: CpfCode },
company: { flex: 2, field: 'company', headerName: 'Company' },
ordinaryAmount: { flex: 2, field: 'ordinaryAmount', headerName: 'Ordinary', type: 'number', valueFormatter: formatDecimal, cellClassName: getColourClassForValue },
specialAmount: { flex: 2, field: 'specialAmount', headerName: 'Special', type: 'number', valueFormatter: formatDecimal, cellClassName: getColourClassForValue },
diff --git a/src/util/cpf-codes.js b/src/util/cpf-codes.js
new file mode 100644
index 0000000..8932b3c
--- /dev/null
+++ b/src/util/cpf-codes.js
@@ -0,0 +1,39 @@
+export const cpfCodes = [
+ { code: 'CON', description: 'Contributions' },
+ { code: 'HSE', description: 'HDB flats and other residential properties' },
+ { code: 'PMI', description: 'Private Medical Insurance' },
+ { code: 'DPS', description: 'Dependants\' Protection Scheme' },
+ { code: 'CSL', description: 'CareShield Life' },
+ { code: 'ADJ', description: 'Adjustment / Interest on recovered CPF contributions' },
+ { code: 'AMP', description: 'Additional Monthly Payout' },
+ { code: 'BAL', description: 'Balance' },
+ { code: 'CLA', description: 'CPF LIFE Annuity Premium' },
+ { code: 'CLB', description: 'CPF LIFE Bonus (L-Bonus)' },
+ { code: 'CLC', description: 'CPF LIFE Change of plan' },
+ { code: 'CLI', description: 'CPF LIFE Payout' },
+ { code: 'CLR', description: 'Refund from CPF LIFE' },
+ { code: 'CLS', description: 'CPF LIFE Switch of plan' },
+ { code: 'DPC', description: 'Dependants\' Protection Scheme return of premium refund' },
+ { code: 'DPR', description: 'Dependants\' Protection Scheme premium refund' },
+ { code: 'DPP', description: 'DPS Fund privatisation residual distribution' },
+ { code: 'DVB', description: 'Deferment and/or Voluntary Deferment Bonus' },
+ { code: 'EDN', description: 'Education Scheme' },
+ { code: 'ESH', description: 'ElderShield' },
+ { code: 'HPR', description: 'Home Protection Scheme Rebate' },
+ { code: 'HPS', description: 'Home Protection Scheme' },
+ { code: 'HRF', description: 'Withdrawal from balance of housing refund' },
+ { code: 'INT', description: 'Interest for the whole year' },
+ { code: 'INV', description: 'CPF Investment Scheme' },
+ { code: 'MED', description: 'Medisave' },
+ { code: 'MSH', description: 'MediShield' },
+ { code: 'MSL', description: 'MediShield Life' },
+ { code: 'MGS', description: 'Medical Grounds Scheme' },
+ { code: 'PTY', description: 'Non-residential property' },
+ { code: 'RFD', description: 'Refund of overpaid contributions' },
+ { code: 'RSS', description: 'Retirement Sum Scheme' },
+ { code: 'RST', description: 'Top-ups under CPF Retirement Sum Topping-Up Scheme' },
+ { code: 'SNS', description: 'Special Needs Savings Scheme' },
+ { code: 'SE:', description:'Payment by self-employed person' },
+ { code: 'TFR', description: 'Transfer between accounts' },
+ { code: 'WDL', description: 'Withdrawal of CPF savings' },
+];