Compare commits
No commits in common. "5439632fc6f6f75fd2ea807150173821f56b57d8" and "a22c23eb2661a76f6afacb25f62ffddfd71f5d27" have entirely different histories.
5439632fc6
...
a22c23eb26
74
src/db.ts
74
src/db.ts
@ -169,75 +169,14 @@ export function serialiseAmount(quantity: number, commodity: string): string {
|
|||||||
return quantityString + ' ' + commodity;
|
return quantityString + ' ' + commodity;
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseFloatStrict(quantity: string): number {
|
|
||||||
// Parses quantity as a float, throwing error on invalid input
|
|
||||||
if (!/^[0-9]+(\.[0-9]+)?$/.test(quantity)) {
|
|
||||||
throw new DeserialiseAmountError('Invalid quantity: ' + quantity);
|
|
||||||
}
|
|
||||||
return parseFloat(quantity);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function validateCommodity(commodity: string) {
|
|
||||||
// Validate that the commodity is correctly formed
|
|
||||||
const commodityParts = commodity.split(' ');
|
|
||||||
if (commodityParts.length > 2) {
|
|
||||||
throw new DeserialiseAmountError('Invalid commodity (more spaces than expected): ' + commodity);
|
|
||||||
}
|
|
||||||
if (commodityParts.length === 2) {
|
|
||||||
// Validate that the second part is a cost basis
|
|
||||||
if (commodityParts[1].startsWith('{{') && commodityParts[1].endsWith('}}')) {
|
|
||||||
const costBase = commodityParts[1].substring(2, commodityParts[1].length - 2);
|
|
||||||
parseFloatStrict(costBase);
|
|
||||||
} else if (commodityParts[1].startsWith('{') && commodityParts[1].endsWith('}')) {
|
|
||||||
const costBase = commodityParts[1].substring(1, commodityParts[1].length - 1);
|
|
||||||
parseFloatStrict(costBase);
|
|
||||||
} else {
|
|
||||||
throw new DeserialiseAmountError('Invalid cost base: ' + commodityParts[1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deserialiseAmount(amount: string): { quantity: number, commodity: string } {
|
export function deserialiseAmount(amount: string): { quantity: number, commodity: string } {
|
||||||
const factor = Math.pow(10, db.metadata.dps);
|
const factor = Math.pow(10, db.metadata.dps);
|
||||||
|
|
||||||
if (amount.length === 0) {
|
|
||||||
throw new DeserialiseAmountError('Amount cannot be blank');
|
|
||||||
}
|
|
||||||
|
|
||||||
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') {
|
|
||||||
throw new DeserialiseAmountError('Invalid quantity: ' + amount + ' (expected quantity after single-letter commodity symbol ' + amount.charAt(0) + ')');
|
|
||||||
}
|
|
||||||
|
|
||||||
let quantity, commodity;
|
|
||||||
|
|
||||||
if (amount.indexOf(' ') < 0) {
|
|
||||||
// No cost base
|
|
||||||
quantity = Math.round(parseFloatStrict(amount.substring(1)) * factor);
|
|
||||||
commodity = amount.charAt(0);
|
|
||||||
} else {
|
|
||||||
// Cost base specified
|
|
||||||
quantity = Math.round(parseFloatStrict(amount.substring(1, amount.indexOf(' '))) * factor);
|
|
||||||
commodity = amount.charAt(0) + amount.substring(amount.indexOf(' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Number.isSafeInteger(quantity)) { throw new DeserialiseAmountError('Quantity not representable by safe integer: ' + amount); }
|
|
||||||
validateCommodity(commodity);
|
|
||||||
|
|
||||||
return {
|
|
||||||
'quantity': quantity,
|
|
||||||
'commodity': commodity
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (amount.indexOf(' ') < 0) {
|
if (amount.indexOf(' ') < 0) {
|
||||||
// Default commodity
|
// Default commodity
|
||||||
const quantity = Math.round(parseFloatStrict(amount) * factor);
|
const quantity = Math.round(parseFloat(amount) * factor)
|
||||||
|
|
||||||
|
if (Number.isNaN(quantity)) { throw new DeserialiseAmountError('Invalid quantity: ' + amount); }
|
||||||
if (!Number.isSafeInteger(quantity)) { throw new DeserialiseAmountError('Quantity not representable by safe integer: ' + amount); }
|
if (!Number.isSafeInteger(quantity)) { throw new DeserialiseAmountError('Quantity not representable by safe integer: ' + amount); }
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -246,14 +185,15 @@ export function deserialiseAmount(amount: string): { quantity: number, commodity
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be multi-letter commodity
|
// FIXME: Parse single letter commodities
|
||||||
const quantityStr = amount.substring(0, amount.indexOf(' '));
|
|
||||||
const quantity = Math.round(parseFloatStrict(quantityStr) * factor)
|
|
||||||
|
|
||||||
|
const quantityStr = amount.substring(0, amount.indexOf(' '));
|
||||||
|
const quantity = Math.round(parseFloat(quantityStr) * factor)
|
||||||
|
|
||||||
|
if (Number.isNaN(quantity)) { throw new DeserialiseAmountError('Invalid quantity: ' + amount); }
|
||||||
if (!Number.isSafeInteger(quantity)) { throw new DeserialiseAmountError('Quantity not representable by safe integer: ' + amount); }
|
if (!Number.isSafeInteger(quantity)) { throw new DeserialiseAmountError('Quantity not representable by safe integer: ' + amount); }
|
||||||
|
|
||||||
const commodity = amount.substring(amount.indexOf(' ') + 1);
|
const commodity = amount.substring(amount.indexOf(' ') + 1);
|
||||||
validateCommodity(commodity);
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
'quantity': quantity,
|
'quantity': quantity,
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
DrCr: Web-based double-entry bookkeeping framework
|
DrCr: Web-based double-entry bookkeeping framework
|
||||||
Copyright (C) 2022-2025 Lee Yingtong Li (RunasSudo)
|
Copyright (C) 2022–2025 Lee Yingtong Li (RunasSudo)
|
||||||
|
|
||||||
This program is free software: you can redistribute it and/or modify
|
This program is free software: you can redistribute it and/or modify
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
@ -16,7 +16,7 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { db, validateCommodity } from './db.ts';
|
import { db } from './db.ts';
|
||||||
|
|
||||||
export function pp(quantity: number): string {
|
export function pp(quantity: number): string {
|
||||||
// Pretty print the quantity
|
// Pretty print the quantity
|
||||||
@ -33,16 +33,8 @@ export function pp(quantity: number): string {
|
|||||||
|
|
||||||
export function ppWithCommodity(quantity: number, commodity: string): string {
|
export function ppWithCommodity(quantity: number, commodity: string): string {
|
||||||
// Pretty print the amount including commodity
|
// Pretty print the amount including commodity
|
||||||
validateCommodity(commodity);
|
if (commodity.length === 1) {
|
||||||
|
return commodity + pp(quantity);
|
||||||
const commodityParts = commodity.split(' ');
|
|
||||||
|
|
||||||
if (commodityParts[0].length === 1) {
|
|
||||||
if (commodityParts.length === 1) {
|
|
||||||
return commodityParts[0] + pp(quantity);
|
|
||||||
} else {
|
|
||||||
return commodityParts[0] + pp(quantity) + ' ' + commodityParts[1];
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
return pp(quantity) + ' ' + commodity;
|
return pp(quantity) + ' ' + commodity;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user