191 lines
4.7 KiB
JavaScript
191 lines
4.7 KiB
JavaScript
export function requiredFields(name, row, fields, update) {
|
|
fields.forEach(field => {
|
|
if (update) {
|
|
if (row.hasOwnProperty(field) && row[field] == null) {
|
|
throw new Error(`${name} is missing field ${field}`);
|
|
}
|
|
} else {
|
|
if (!row.hasOwnProperty(field) || row[field] == null) {
|
|
throw new Error(`${name} is missing field ${field}`);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
export function toDateRepr(str) {
|
|
if (typeof str !== 'string') {
|
|
throw new Error('toDateRepr not passed a string: ' + str);
|
|
}
|
|
|
|
return parseInt(str.replace(/-/g, ''));
|
|
}
|
|
|
|
export function fromDateRepr(number) {
|
|
if (typeof number !== 'number') {
|
|
throw new Error('fromDateRepr not passed a number: ' + number);
|
|
}
|
|
|
|
const dateString = number.toString();
|
|
return (
|
|
dateString.slice(0, 4) +
|
|
'-' +
|
|
dateString.slice(4, 6) +
|
|
'-' +
|
|
dateString.slice(6)
|
|
);
|
|
}
|
|
|
|
export const accountModel = {
|
|
validateAccountType(account) {
|
|
const { type } = account;
|
|
if (
|
|
type !== 'checking' &&
|
|
type !== 'savings' &&
|
|
type !== 'investment' &&
|
|
type !== 'credit' &&
|
|
type !== 'mortgage' &&
|
|
type !== 'debt' &&
|
|
type !== 'other'
|
|
) {
|
|
throw new Error('Invalid account type: ' + type);
|
|
}
|
|
},
|
|
|
|
validate(account, { update } = {}) {
|
|
if (!update || account.type != null) {
|
|
accountModel.validateAccountType(account);
|
|
}
|
|
|
|
requiredFields(
|
|
'account',
|
|
account,
|
|
update ? ['name', 'type', 'offbudget', 'closed'] : ['name', 'type'],
|
|
update
|
|
);
|
|
|
|
return account;
|
|
}
|
|
};
|
|
|
|
export const categoryModel = {
|
|
validate(category, { update } = {}) {
|
|
requiredFields(
|
|
'category',
|
|
category,
|
|
update ? ['name', 'is_income', 'cat_group'] : ['name', 'cat_group'],
|
|
update
|
|
);
|
|
|
|
let { sort_order, ...rest } = category;
|
|
return rest;
|
|
}
|
|
};
|
|
|
|
export const categoryGroupModel = {
|
|
validate(categoryGroup, { update } = {}) {
|
|
requiredFields(
|
|
'categoryGroup',
|
|
categoryGroup,
|
|
update ? ['name', 'is_income'] : ['name'],
|
|
update
|
|
);
|
|
|
|
let { sort_order, ...rest } = categoryGroup;
|
|
return rest;
|
|
}
|
|
};
|
|
|
|
export const payeeModel = {
|
|
validate(payee, { update } = {}) {
|
|
requiredFields('payee', payee, ['name'], update);
|
|
return payee;
|
|
}
|
|
};
|
|
|
|
export const payeeRuleModel = {
|
|
validateType(rule) {
|
|
const { type } = rule;
|
|
if (type !== 'equals' && type !== 'contains') {
|
|
throw new Error('Invalid rule type: ' + type);
|
|
}
|
|
},
|
|
|
|
validate(rule, { update } = {}) {
|
|
if (!update || 'type' in rule) {
|
|
payeeRuleModel.validateType(rule);
|
|
}
|
|
|
|
requiredFields('payee_rules', rule, ['payee_id', 'type'], update);
|
|
return rule;
|
|
}
|
|
};
|
|
|
|
export const transactionModel = {
|
|
validate(trans, { update } = {}) {
|
|
requiredFields('transaction', trans, ['date', 'acct'], update);
|
|
|
|
if ('date' in trans) {
|
|
// Make sure it's the right format, and also do a sanity check.
|
|
// Really old dates can mess up the system and can happen by
|
|
// accident
|
|
if (
|
|
trans.date.match(/^\d{4}-\d{2}-\d{2}$/) == null ||
|
|
trans.date < '2000-01-01'
|
|
) {
|
|
throw new Error('Invalid transaction date: ' + trans.date);
|
|
}
|
|
}
|
|
|
|
return trans;
|
|
},
|
|
|
|
toJS(row) {
|
|
// Check a non-important field that typically wouldn't be passed in
|
|
// manually, and use it as a smoke test to see if this is a
|
|
// fully-formed transaction or not.
|
|
if (!('location' in row)) {
|
|
throw new Error(
|
|
'A full transaction is required to be passed to `toJS`. Instead got: ' +
|
|
JSON.stringify(row)
|
|
);
|
|
}
|
|
|
|
let trans = { ...row };
|
|
trans.error = row.error ? JSON.parse(row.error) : null;
|
|
trans.isParent = row.isParent === 1 ? true : false;
|
|
trans.isChild = row.isChild === 1 ? true : false;
|
|
trans.starting_balance_flag =
|
|
row.starting_balance_flag === 1 ? true : false;
|
|
trans.cleared = row.cleared === 1 ? true : false;
|
|
trans.pending = row.pending === 1 ? true : false;
|
|
trans.date = trans.date && fromDateRepr(trans.date);
|
|
return trans;
|
|
},
|
|
|
|
fromJS(trans) {
|
|
let row = { ...trans };
|
|
if ('error' in row) {
|
|
row.error = trans.error ? JSON.stringify(trans.error) : null;
|
|
}
|
|
if ('isParent' in row) {
|
|
row.isParent = trans.isParent ? 1 : 0;
|
|
}
|
|
if ('isChild' in row) {
|
|
row.isChild = trans.isChild ? 1 : 0;
|
|
}
|
|
if ('cleared' in row) {
|
|
row.cleared = trans.cleared ? 1 : 0;
|
|
}
|
|
if ('pending' in row) {
|
|
row.pending = trans.pending ? 1 : 0;
|
|
}
|
|
if ('starting_balance_flag' in row) {
|
|
row.starting_balance_flag = trans.starting_balance_flag ? 1 : 0;
|
|
}
|
|
if ('date' in row) {
|
|
row.date = toDateRepr(trans.date);
|
|
}
|
|
|
|
return row;
|
|
}
|
|
};
|