mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-11 12:43:09 -05:00
Enhancement: Add option to select in/out field during import (#1788)
This commit is contained in:
@@ -193,11 +193,22 @@ function getInitialMappings(transactions) {
|
||||
),
|
||||
);
|
||||
|
||||
let inOutField = key(
|
||||
fields.find(
|
||||
([name, value]) =>
|
||||
name !== dateField &&
|
||||
name !== amountField &&
|
||||
name !== payeeField &&
|
||||
name !== notesField,
|
||||
),
|
||||
);
|
||||
|
||||
return {
|
||||
date: dateField,
|
||||
amount: amountField,
|
||||
payee: payeeField,
|
||||
notes: notesField,
|
||||
inOut: inOutField,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -222,7 +233,14 @@ function parseAmount(amount, mapper) {
|
||||
return value;
|
||||
}
|
||||
|
||||
function parseAmountFields(trans, splitMode, flipAmount, multiplierAmount) {
|
||||
function parseAmountFields(
|
||||
trans,
|
||||
splitMode,
|
||||
inOutMode,
|
||||
outValue,
|
||||
flipAmount,
|
||||
multiplierAmount,
|
||||
) {
|
||||
const multiplier = parseFloat(multiplierAmount) || 1.0;
|
||||
|
||||
if (splitMode) {
|
||||
@@ -240,6 +258,16 @@ function parseAmountFields(trans, splitMode, flipAmount, multiplierAmount) {
|
||||
inflow,
|
||||
};
|
||||
}
|
||||
if (inOutMode) {
|
||||
return {
|
||||
amount:
|
||||
parseAmount(trans.amount, n =>
|
||||
trans.inOut === outValue ? Math.abs(n) * -1 : Math.abs(n),
|
||||
) * multiplier,
|
||||
outflow: null,
|
||||
inflow: null,
|
||||
};
|
||||
}
|
||||
return {
|
||||
amount:
|
||||
parseAmount(trans.amount, n => (flipAmount ? n * -1 : n)) * multiplier,
|
||||
@@ -255,6 +283,8 @@ function Transaction({
|
||||
parseDateFormat,
|
||||
dateFormat,
|
||||
splitMode,
|
||||
inOutMode,
|
||||
outValue,
|
||||
flipAmount,
|
||||
multiplierAmount,
|
||||
}) {
|
||||
@@ -269,6 +299,8 @@ function Transaction({
|
||||
let { amount, outflow, inflow } = parseAmountFields(
|
||||
transaction,
|
||||
splitMode,
|
||||
inOutMode,
|
||||
outValue,
|
||||
flipAmount,
|
||||
multiplierAmount,
|
||||
);
|
||||
@@ -320,13 +352,24 @@ function Transaction({
|
||||
</Field>
|
||||
</>
|
||||
) : (
|
||||
<Field
|
||||
width={90}
|
||||
contentStyle={{ textAlign: 'right', ...styles.tnum }}
|
||||
title={amount}
|
||||
>
|
||||
{amount}
|
||||
</Field>
|
||||
<>
|
||||
{inOutMode && (
|
||||
<Field
|
||||
width={90}
|
||||
contentStyle={{ textAlign: 'left', ...styles.tnum }}
|
||||
title={transaction.inOut}
|
||||
>
|
||||
{transaction.inOut}
|
||||
</Field>
|
||||
)}
|
||||
<Field
|
||||
width={90}
|
||||
contentStyle={{ textAlign: 'right', ...styles.tnum }}
|
||||
title={amount}
|
||||
>
|
||||
{amount}
|
||||
</Field>
|
||||
</>
|
||||
)}
|
||||
</Row>
|
||||
);
|
||||
@@ -457,11 +500,43 @@ function MultiplierOption({
|
||||
);
|
||||
}
|
||||
|
||||
function InOutOption({
|
||||
inOutMode,
|
||||
outValue,
|
||||
disabled,
|
||||
onToggle,
|
||||
onChangeText,
|
||||
}) {
|
||||
return (
|
||||
<View style={{ flexDirection: 'row', gap: 10, height: 28 }}>
|
||||
<CheckboxOption
|
||||
id="form_inOut"
|
||||
checked={inOutMode}
|
||||
disabled={disabled}
|
||||
onChange={onToggle}
|
||||
>
|
||||
{inOutMode
|
||||
? 'in/out identifier'
|
||||
: 'Select column to specify if amount goes in/out'}
|
||||
</CheckboxOption>
|
||||
{inOutMode && (
|
||||
<Input
|
||||
type="text"
|
||||
value={outValue}
|
||||
onUpdate={onChangeText}
|
||||
placeholder="Value for out rows, i.e. Credit"
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function FieldMappings({
|
||||
transactions,
|
||||
mappings,
|
||||
onChange,
|
||||
splitMode,
|
||||
inOutMode,
|
||||
hasHeaderRow,
|
||||
}) {
|
||||
if (transactions.length === 0) {
|
||||
@@ -537,16 +612,30 @@ function FieldMappings({
|
||||
</View>
|
||||
</>
|
||||
) : (
|
||||
<View style={{ flex: 1 }}>
|
||||
<SubLabel title="Amount" />
|
||||
<SelectField
|
||||
options={options}
|
||||
value={mappings.amount}
|
||||
onChange={name => onChange('amount', name)}
|
||||
hasHeaderRow={hasHeaderRow}
|
||||
firstTransaction={transactions[0]}
|
||||
/>
|
||||
</View>
|
||||
<>
|
||||
{inOutMode && (
|
||||
<View style={{ flex: 1 }}>
|
||||
<SubLabel title="In/Out" />
|
||||
<SelectField
|
||||
options={options}
|
||||
value={mappings.inOut}
|
||||
onChange={name => onChange('inOut', name)}
|
||||
hasHeaderRow={hasHeaderRow}
|
||||
firstTransaction={transactions[0]}
|
||||
/>
|
||||
</View>
|
||||
)}
|
||||
<View style={{ flex: 1 }}>
|
||||
<SubLabel title="Amount" />
|
||||
<SelectField
|
||||
options={options}
|
||||
value={mappings.amount}
|
||||
onChange={name => onChange('amount', name)}
|
||||
hasHeaderRow={hasHeaderRow}
|
||||
firstTransaction={transactions[0]}
|
||||
/>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</Stack>
|
||||
</View>
|
||||
@@ -569,6 +658,8 @@ export default function ImportTransactions({ modalProps, options }) {
|
||||
let [filetype, setFileType] = useState(null);
|
||||
let [fieldMappings, setFieldMappings] = useState(null);
|
||||
let [splitMode, setSplitMode] = useState(false);
|
||||
let [inOutMode, setInOutMode] = useState(false);
|
||||
let [outValue, setOutValue] = useState('');
|
||||
let [flipAmount, setFlipAmount] = useState(false);
|
||||
let [multiplierEnabled, setMultiplierEnabled] = useState(false);
|
||||
let { accountId, onImported } = options;
|
||||
@@ -684,6 +775,8 @@ export default function ImportTransactions({ modalProps, options }) {
|
||||
|
||||
let isSplit = !splitMode;
|
||||
setSplitMode(isSplit);
|
||||
setInOutMode(false);
|
||||
setFlipAmount(false);
|
||||
|
||||
// Run auto-detection on the fields to try to detect the fields
|
||||
// automatically
|
||||
@@ -749,6 +842,8 @@ export default function ImportTransactions({ modalProps, options }) {
|
||||
let { amount } = parseAmountFields(
|
||||
trans,
|
||||
splitMode,
|
||||
inOutMode,
|
||||
outValue,
|
||||
flipAmount,
|
||||
multiplierAmount,
|
||||
);
|
||||
@@ -757,7 +852,7 @@ export default function ImportTransactions({ modalProps, options }) {
|
||||
break;
|
||||
}
|
||||
|
||||
let { inflow, outflow, ...finalTransaction } = trans;
|
||||
let { inflow, outflow, inOut, ...finalTransaction } = trans;
|
||||
finalTransactions.push({
|
||||
...finalTransaction,
|
||||
date,
|
||||
@@ -812,6 +907,9 @@ export default function ImportTransactions({ modalProps, options }) {
|
||||
{ name: 'Notes', width: 'flex' },
|
||||
];
|
||||
|
||||
if (inOutMode) {
|
||||
headers.push({ name: 'In/Out', width: 90, style: { textAlign: 'left' } });
|
||||
}
|
||||
if (splitMode) {
|
||||
headers.push({ name: 'Outflow', width: 90, style: { textAlign: 'right' } });
|
||||
headers.push({ name: 'Inflow', width: 90, style: { textAlign: 'right' } });
|
||||
@@ -873,6 +971,8 @@ export default function ImportTransactions({ modalProps, options }) {
|
||||
dateFormat={dateFormat}
|
||||
fieldMappings={fieldMappings}
|
||||
splitMode={splitMode}
|
||||
inOutMode={inOutMode}
|
||||
outValue={outValue}
|
||||
flipAmount={flipAmount}
|
||||
multiplierAmount={multiplierAmount}
|
||||
/>
|
||||
@@ -905,6 +1005,7 @@ export default function ImportTransactions({ modalProps, options }) {
|
||||
onChange={onUpdateFields}
|
||||
mappings={fieldMappings}
|
||||
splitMode={splitMode}
|
||||
inOutMode={inOutMode}
|
||||
hasHeaderRow={hasHeaderRow}
|
||||
/>
|
||||
</View>
|
||||
@@ -1018,19 +1119,29 @@ export default function ImportTransactions({ modalProps, options }) {
|
||||
<CheckboxOption
|
||||
id="form_flip"
|
||||
checked={flipAmount}
|
||||
disabled={splitMode}
|
||||
disabled={splitMode || inOutMode}
|
||||
onChange={() => setFlipAmount(!flipAmount)}
|
||||
>
|
||||
Flip amount
|
||||
</CheckboxOption>
|
||||
{filetype === 'csv' && (
|
||||
<CheckboxOption
|
||||
id="form_split"
|
||||
checked={splitMode}
|
||||
onChange={onSplitMode}
|
||||
>
|
||||
Split amount into separate inflow/outflow columns
|
||||
</CheckboxOption>
|
||||
<>
|
||||
<CheckboxOption
|
||||
id="form_split"
|
||||
checked={splitMode}
|
||||
disabled={inOutMode || flipAmount}
|
||||
onChange={onSplitMode}
|
||||
>
|
||||
Split amount into separate inflow/outflow columns
|
||||
</CheckboxOption>
|
||||
<InOutOption
|
||||
inOutMode={inOutMode}
|
||||
outValue={outValue}
|
||||
disabled={splitMode || flipAmount}
|
||||
onToggle={() => setInOutMode(!inOutMode)}
|
||||
onChangeText={setOutValue}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<MultiplierOption
|
||||
multiplierEnabled={multiplierEnabled}
|
||||
|
||||
6
upcoming-release-notes/1788.md
Normal file
6
upcoming-release-notes/1788.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: Enhancements
|
||||
authors: [Jessseee]
|
||||
---
|
||||
|
||||
Added option to select in/out field during import.
|
||||
Reference in New Issue
Block a user