Compare commits
No commits in common. "a66f90e0c9c318142dd0f34d8bc348221957347e" and "3badc1e93642a02e2dfdfef5bdc1e8d47836d16b" have entirely different histories.
a66f90e0c9
...
3badc1e936
@ -830,7 +830,7 @@ impl ReportingStep for CurrentYearEarningsToEquity {
|
||||
account: account.clone(),
|
||||
quantity: -balance,
|
||||
commodity: context.reporting_commodity.clone(),
|
||||
quantity_ascost: Some(-balance),
|
||||
quantity_ascost: None,
|
||||
},
|
||||
Posting {
|
||||
id: None,
|
||||
@ -839,7 +839,7 @@ impl ReportingStep for CurrentYearEarningsToEquity {
|
||||
account: crate::CURRENT_YEAR_EARNINGS.to_string(),
|
||||
quantity: *balance,
|
||||
commodity: context.reporting_commodity.clone(),
|
||||
quantity_ascost: Some(*balance),
|
||||
quantity_ascost: None,
|
||||
},
|
||||
],
|
||||
})
|
||||
@ -1396,7 +1396,7 @@ impl ReportingStep for RetainedEarningsToEquity {
|
||||
account: account.clone(),
|
||||
quantity: -balance,
|
||||
commodity: context.reporting_commodity.clone(),
|
||||
quantity_ascost: Some(-balance),
|
||||
quantity_ascost: None,
|
||||
},
|
||||
Posting {
|
||||
id: None,
|
||||
@ -1405,7 +1405,7 @@ impl ReportingStep for RetainedEarningsToEquity {
|
||||
account: crate::RETAINED_EARNINGS.to_string(),
|
||||
quantity: *balance,
|
||||
commodity: context.reporting_commodity.clone(),
|
||||
quantity_ascost: Some(*balance),
|
||||
quantity_ascost: None,
|
||||
},
|
||||
],
|
||||
})
|
||||
|
24
src/db.ts
24
src/db.ts
@ -183,7 +183,7 @@ export function serialiseAmount(quantity: number, commodity: string): string {
|
||||
|
||||
function parseFloatStrict(quantity: string): number {
|
||||
// Parses quantity as a float, throwing error on invalid input
|
||||
if (!/^-?[0-9]+(\.[0-9]+)?$/.test(quantity)) {
|
||||
if (!/^[0-9]+(\.[0-9]+)?$/.test(quantity)) {
|
||||
throw new DeserialiseAmountError('Invalid quantity: ' + quantity);
|
||||
}
|
||||
return parseFloat(quantity);
|
||||
@ -216,21 +216,12 @@ export function deserialiseAmount(amount: string): { quantity: number, commodity
|
||||
throw new DeserialiseAmountError('Amount cannot be blank');
|
||||
}
|
||||
|
||||
if (amount.charAt(0) === '-') {
|
||||
// Handle negative amount
|
||||
const amountAbs = deserialiseAmount(amount.substring(1));
|
||||
return {
|
||||
quantity: -amountAbs.quantity,
|
||||
commodity: amountAbs.commodity
|
||||
};
|
||||
}
|
||||
|
||||
if (amount.charAt(0) < '0' || amount.charAt(0) > '9') {
|
||||
// Check for single letter commodity
|
||||
if (amount.length === 1) {
|
||||
throw new DeserialiseAmountError('Quantity cannot be blank (expected quantity after commodity symbol ' + amount + ')');
|
||||
}
|
||||
if ((amount.charAt(1) < '0' || amount.charAt(1) > '9') && amount.charAt(1) !== '-') {
|
||||
if (amount.charAt(1) < '0' || amount.charAt(1) > '9') {
|
||||
throw new DeserialiseAmountError('Invalid quantity: ' + amount + ' (expected quantity after single-letter commodity symbol ' + amount.charAt(0) + ')');
|
||||
}
|
||||
|
||||
@ -326,17 +317,6 @@ export interface JoinedTransactionPosting {
|
||||
running_balance?: number
|
||||
}
|
||||
|
||||
export function postingQuantityAsCost(posting: Posting | JoinedTransactionPosting) {
|
||||
// Convert the posting amount to cost price in the reporting commodity
|
||||
|
||||
if (posting.quantity_ascost) {
|
||||
return posting.quantity_ascost;
|
||||
} else {
|
||||
// NB: This branch is rarely taken - most conversions are performed in SQL via the transactions_with_quantity_ascost view
|
||||
return asCost(posting.quantity, posting.commodity);
|
||||
}
|
||||
}
|
||||
|
||||
export interface StatementLine {
|
||||
id: number | null,
|
||||
source_account: string,
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
DrCr: Double-entry bookkeeping framework
|
||||
DrCr: Web-based double-entry bookkeeping framework
|
||||
Copyright (C) 2022-2025 Lee Yingtong Li (RunasSudo)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
@ -66,7 +66,7 @@
|
||||
import { UnlistenFn, listen } from '@tauri-apps/api/event';
|
||||
import { onUnmounted, ref, watch } from 'vue';
|
||||
|
||||
import { Transaction, postingQuantityAsCost } from '../db.ts';
|
||||
import { Transaction } from '../db.ts';
|
||||
import { pp, ppWithCommodity } from '../display.ts';
|
||||
import { renderComponent } from '../webutil.ts';
|
||||
|
||||
@ -124,10 +124,10 @@
|
||||
<td class="py-0.5 px-1 text-gray-900 text-end"><i>${ posting.quantity >= 0 ? 'Dr' : 'Cr' }</i></td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[30%]"><a href="/transactions/${ encodeURIComponent(posting.account) }" class="text-gray-900 hover:text-blue-700 hover:underline">${ posting.account }</a></td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[12ex] text-end">
|
||||
${ posting.quantity >= 0 ? pp(postingQuantityAsCost(posting)) : '' }
|
||||
${ posting.quantity >= 0 ? pp(posting.quantity_ascost!) : '' }
|
||||
</td>
|
||||
<td class="py-0.5 pl-1 text-gray-900 lg:w-[12ex] text-end">
|
||||
${ posting.quantity < 0 ? pp(-postingQuantityAsCost(posting)) : '' }
|
||||
${ posting.quantity < 0 ? pp(-posting.quantity_ascost!) : '' }
|
||||
</td>
|
||||
</tr>`
|
||||
);
|
||||
|
@ -49,7 +49,7 @@
|
||||
import { onMounted, onUnmounted, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
import { Transaction, postingQuantityAsCost } from '../db.ts';
|
||||
import { Transaction } from '../db.ts';
|
||||
import { pp } from '../display.ts';
|
||||
import { renderComponent } from '../webutil.ts';
|
||||
|
||||
@ -66,7 +66,7 @@
|
||||
const transaction = transactions[i];
|
||||
for (const posting of transaction.postings) {
|
||||
if (posting.account === route.params.account) {
|
||||
balance += postingQuantityAsCost(posting);
|
||||
balance += posting.quantity_ascost!;
|
||||
posting.running_balance = balance;
|
||||
}
|
||||
}
|
||||
@ -100,8 +100,8 @@
|
||||
<td class="py-0.5 pr-1 text-gray-900 lg:w-[12ex]">${ dayjs(transaction.dt).format('YYYY-MM-DD') }</td>
|
||||
<td class="py-0.5 px-1 text-gray-900">${ transaction.description } ${ editLink }</td>
|
||||
<td class="py-0.5 px-1 text-gray-900"><a href="/transactions/${ encodeURIComponent(otherAccountPosting!.account) }" class="text-gray-900 hover:text-blue-700 hover:underline">${ otherAccountPosting!.account }</a></td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[12ex] text-end">${ thisAccountPosting!.quantity >= 0 ? pp(postingQuantityAsCost(thisAccountPosting!)) : '' }</td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[12ex] text-end">${ thisAccountPosting!.quantity < 0 ? pp(-postingQuantityAsCost(thisAccountPosting!)) : '' }</td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[12ex] text-end">${ thisAccountPosting!.quantity >= 0 ? pp(thisAccountPosting!.quantity_ascost!) : '' }</td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[12ex] text-end">${ thisAccountPosting!.quantity < 0 ? pp(-thisAccountPosting!.quantity_ascost!) : '' }</td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[12ex] text-end">${ pp(Math.abs(thisAccountPosting!.running_balance!)) }</td>
|
||||
<td class="py-0.5 text-gray-900">${ thisAccountPosting!.running_balance! >= 0 ? 'Dr' : 'Cr' }</td>
|
||||
</tr>`
|
||||
@ -125,8 +125,8 @@
|
||||
<td></td>
|
||||
<td class="py-0.5 px-1 text-gray-900 text-end"><i>${ posting.quantity >= 0 ? 'Dr' : 'Cr' }</i></td>
|
||||
<td class="py-0.5 px-1 text-gray-900"><a href="/transactions/${ encodeURIComponent(posting.account) }" class="text-gray-900 hover:text-blue-700 hover:underline">${ posting.account }</a></td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[12ex] text-end">${ posting.quantity >= 0 ? pp(postingQuantityAsCost(posting)) : '' }</td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[12ex] text-end">${ posting.quantity < 0 ? pp(-postingQuantityAsCost(posting)) : '' }</td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[12ex] text-end">${ posting.quantity >= 0 ? pp(posting.quantity_ascost!) : '' }</td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[12ex] text-end">${ posting.quantity < 0 ? pp(-posting.quantity_ascost!) : '' }</td>
|
||||
<td class="py-0.5 px-1 text-gray-900 lg:w-[12ex] text-end">${ posting.account === route.params.account ? pp(Math.abs(posting.running_balance!)) : '' }</td>
|
||||
<td class="py-0.5 text-gray-900">${ posting.account === route.params.account ? (posting.running_balance! >= 0 ? 'Dr' : 'Cr') : '' }</td>
|
||||
</tr>`
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
DrCr: Double-entry bookkeeping framework
|
||||
DrCr: Web-based double-entry bookkeeping framework
|
||||
Copyright (C) 2022-2025 Lee Yingtong Li (RunasSudo)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
@ -46,7 +46,13 @@
|
||||
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<span class="text-gray-500">{{ db.metadata.reporting_commodity }}</span>
|
||||
</div>
|
||||
<input type="number" class="bordered-field pl-7" step="0.01" v-model="adjustment.cost_adjustment" placeholder="0.00">
|
||||
<input type="number" class="bordered-field pl-7 pr-16" step="0.01" v-model="adjustment.cost_adjustment_abs" placeholder="0.00">
|
||||
<div class="absolute inset-y-0 right-0 flex items-center">
|
||||
<select class="h-full border-0 bg-transparent py-0 pl-2 pr-8 text-gray-900 focus:ring-2 focus:ring-inset focus:ring-indigo-600" v-model="adjustment.sign">
|
||||
<option value="dr">Dr</option>
|
||||
<option value="cr">Cr</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -84,7 +90,8 @@
|
||||
acquisition_dt: string,
|
||||
dt: string,
|
||||
description: string,
|
||||
cost_adjustment: string,
|
||||
sign: string,
|
||||
cost_adjustment_abs: string,
|
||||
}
|
||||
|
||||
const { adjustment } = defineProps<{ adjustment: EditingCGTAdjustment }>();
|
||||
@ -107,9 +114,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
let cost_adjustment;
|
||||
let cost_adjustment_abs;
|
||||
try {
|
||||
cost_adjustment = deserialiseAmount('' + adjustment.cost_adjustment).quantity;
|
||||
cost_adjustment_abs = deserialiseAmount('' + adjustment.cost_adjustment_abs);
|
||||
} catch (err) {
|
||||
if (err instanceof DeserialiseAmountError) {
|
||||
error.value = err.message;
|
||||
@ -119,6 +126,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
const cost_adjustment = adjustment.sign === 'dr' ? cost_adjustment_abs.quantity : -cost_adjustment_abs.quantity;
|
||||
|
||||
const session = await db.load();
|
||||
|
||||
if (adjustment.id === null) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
DrCr: Double-entry bookkeeping framework
|
||||
DrCr: Web-based double-entry bookkeeping framework
|
||||
Copyright (C) 2022-2025 Lee Yingtong Li (RunasSudo)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
@ -41,7 +41,8 @@
|
||||
acquisition_dt: null!,
|
||||
dt: null!,
|
||||
description: null!,
|
||||
cost_adjustment: null!,
|
||||
sign: null!,
|
||||
cost_adjustment_abs: null!,
|
||||
} as EditingCGTAdjustment);
|
||||
|
||||
async function load() {
|
||||
@ -59,7 +60,8 @@
|
||||
rawAdjustment.asset = serialiseAmount(rawAdjustment.quantity, rawAdjustment.commodity);
|
||||
rawAdjustment.acquisition_dt = dayjs(rawAdjustment.acquisition_dt).format('YYYY-MM-DD');
|
||||
rawAdjustment.dt = dayjs(rawAdjustment.dt).format('YYYY-MM-DD');
|
||||
rawAdjustment.cost_adjustment = serialiseAmount(rawAdjustment.cost_adjustment, db.metadata.reporting_commodity);
|
||||
rawAdjustment.sign = rawAdjustment.cost_adjustment >= 0 ? 'dr' : 'cr';
|
||||
rawAdjustment.cost_adjustment_abs = serialiseAmount(Math.abs(rawAdjustment.cost_adjustment), db.metadata.reporting_commodity);
|
||||
|
||||
adjustment.value = rawAdjustment as EditingCGTAdjustment;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
DrCr: Double-entry bookkeeping framework
|
||||
DrCr: Web-based double-entry bookkeeping framework
|
||||
Copyright (C) 2022-2025 Lee Yingtong Li (RunasSudo)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
@ -57,7 +57,13 @@
|
||||
<div class="pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3">
|
||||
<span class="text-gray-500">{{ db.metadata.reporting_commodity }}</span>
|
||||
</div>
|
||||
<input type="number" class="bordered-field pl-7" step="0.01" v-model="cost_adjustment" placeholder="0.00">
|
||||
<input type="number" class="bordered-field pl-7 pr-16" step="0.01" v-model="cost_adjustment_abs" placeholder="0.00">
|
||||
<div class="absolute inset-y-0 right-0 flex items-center">
|
||||
<select class="h-full border-0 bg-transparent py-0 pl-2 pr-8 text-gray-900 focus:ring-2 focus:ring-inset focus:ring-indigo-600" v-model="sign">
|
||||
<option value="dr">Dr</option>
|
||||
<option value="cr">Cr</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -95,7 +101,8 @@
|
||||
const commodity = ref('');
|
||||
const dt = ref(dayjs().format('YYYY-MM-DD'));
|
||||
const description = ref('');
|
||||
const cost_adjustment = ref(null! as number);
|
||||
const cost_adjustment_abs = ref(null! as number);
|
||||
const sign = ref('dr');
|
||||
|
||||
const error = ref(null as string | null);
|
||||
|
||||
@ -104,9 +111,9 @@
|
||||
|
||||
error.value = null;
|
||||
|
||||
let totalAdjustment;
|
||||
let totalAdjustmentAbs;
|
||||
try {
|
||||
totalAdjustment = deserialiseAmount('' + cost_adjustment.value).quantity;
|
||||
totalAdjustmentAbs = deserialiseAmount('' + cost_adjustment_abs.value);
|
||||
} catch (err) {
|
||||
if (err instanceof DeserialiseAmountError) {
|
||||
error.value = err.message;
|
||||
@ -116,6 +123,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
const totalAdjustment = sign.value === 'dr' ? totalAdjustmentAbs.quantity : -totalAdjustmentAbs.quantity;
|
||||
|
||||
// Get all postings to the CGT asset account
|
||||
const session = await db.load();
|
||||
const cgtPostings = await session.select(
|
||||
|
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
DrCr: Double-entry bookkeeping framework
|
||||
DrCr: Web-based double-entry bookkeeping framework
|
||||
Copyright (C) 2022-2025 Lee Yingtong Li (RunasSudo)
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
@ -37,6 +37,7 @@
|
||||
acquisition_dt: dayjs().format('YYYY-MM-DD'),
|
||||
dt: dayjs().format('YYYY-MM-DD'),
|
||||
description: '',
|
||||
cost_adjustment: '',
|
||||
sign: 'dr',
|
||||
cost_adjustment_abs: '',
|
||||
} as EditingCGTAdjustment);
|
||||
</script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user