support not in options for transaction type, transaction category and account filters

This commit is contained in:
MaysWind
2026-04-11 16:49:11 +08:00
parent a604737c7c
commit 448fc760c0
21 changed files with 123 additions and 33 deletions
+2
View File
@@ -74,6 +74,7 @@ export class TransactionExplorerConditionField implements NameValue {
export enum TransactionExplorerConditionOperatorType {
In = 'in',
NotIn = 'notIn',
GreaterThan = 'greaterThan',
LessThan = 'lessThan',
Equals = 'equals',
@@ -103,6 +104,7 @@ export class TransactionExplorerConditionOperator implements NameValue {
private static readonly allInstancesByValue: Record<string, TransactionExplorerConditionOperator> = {};
public static readonly In = new TransactionExplorerConditionOperator('In', TransactionExplorerConditionOperatorType.In);
public static readonly NotIn = new TransactionExplorerConditionOperator('Not in', TransactionExplorerConditionOperatorType.NotIn);
public static readonly GreaterThan = new TransactionExplorerConditionOperator('Greater than', TransactionExplorerConditionOperatorType.GreaterThan);
public static readonly LessThan = new TransactionExplorerConditionOperator('Less than', TransactionExplorerConditionOperatorType.LessThan);
public static readonly Equals = new TransactionExplorerConditionOperator('Equal to', TransactionExplorerConditionOperatorType.Equals);
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Zwischen",
"Not between": "Nicht zwischen",
"In": "In",
"Not in": "Not in",
"Has any": "Hat eines",
"Has all": "Hat alle",
"Not has any": "Hat keines",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Between",
"Not between": "Not between",
"In": "In",
"Not in": "Not in",
"Has any": "Has any",
"Has all": "Has all",
"Not has any": "Not has any",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Entre",
"Not between": "No entre",
"In": "En",
"Not in": "Not in",
"Has any": "Tiene cualquiera",
"Has all": "Tiene todas",
"Not has any": "No tiene cualquiera",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Entre",
"Not between": "Pas entre",
"In": "In",
"Not in": "Not in",
"Has any": "Has any",
"Has all": "Has all",
"Not has any": "Not has any",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Tra",
"Not between": "Non tra",
"In": "In",
"Not in": "Not in",
"Has any": "Has any",
"Has all": "Has all",
"Not has any": "Not has any",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "間",
"Not between": "間ではない",
"In": "In",
"Not in": "Not in",
"Has any": "Has any",
"Has all": "Has all",
"Not has any": "Not has any",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "ನಡುವೆ",
"Not between": "ನಡುವಲ್ಲ",
"In": "In",
"Not in": "Not in",
"Has any": "Has any",
"Has all": "Has all",
"Not has any": "Not has any",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "사이",
"Not between": "사이 아님",
"In": "In",
"Not in": "Not in",
"Has any": "Has any",
"Has all": "Has all",
"Not has any": "Not has any",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Tussen",
"Not between": "Niet tussen",
"In": "In",
"Not in": "Not in",
"Has any": "Has any",
"Has all": "Has all",
"Not has any": "Not has any",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Entre",
"Not between": "Não entre",
"In": "Em",
"Not in": "Not in",
"Has any": "Contém algum",
"Has all": "Contém todos",
"Not has any": "Não contém nenhum",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Между",
"Not between": "Не между",
"In": "В",
"Not in": "Not in",
"Has any": "Содержит любой",
"Has all": "Содержит все",
"Not has any": "Не содержит любой",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Med",
"Not between": "Ni med",
"In": "V",
"Not in": "Not in",
"Has any": "Vsebuje katerokoli",
"Has all": "Vsebuje vse",
"Not has any": "Ne vsebuje nobene",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "இடையே",
"Not between": "இடையில் இல்லை",
"In": "In",
"Not in": "Not in",
"Has any": "ஏதேனும் உள்ளது",
"Has all": "அனைத்தும் உள்ளது",
"Not has any": "ஏதும் இல்லை",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "ระหว่าง",
"Not between": "ไม่อยู่ระหว่าง",
"In": "In",
"Not in": "Not in",
"Has any": "Has any",
"Has all": "Has all",
"Not has any": "Not has any",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Arasında",
"Not between": "Arasında değil",
"In": "In",
"Not in": "Not in",
"Has any": "Has any",
"Has all": "Has all",
"Not has any": "Not has any",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Між",
"Not between": "Не між",
"In": "In",
"Not in": "Not in",
"Has any": "Has any",
"Has all": "Has all",
"Not has any": "Not has any",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "Giữa",
"Not between": "Không giữa",
"In": "In",
"Not in": "Not in",
"Has any": "Has any",
"Has all": "Has all",
"Not has any": "Not has any",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "介于",
"Not between": "不介于",
"In": "在范围内",
"Not in": "不在范围内",
"Has any": "包含任意",
"Has all": "包含全部",
"Not has any": "不包含任意",
+1
View File
@@ -1558,6 +1558,7 @@
"Between": "介於",
"Not between": "不介於",
"In": "包含於",
"Not in": "不包含於",
"Has any": "包含任一",
"Has all": "包含所有",
"Not has any": "不包含任一",
+102 -33
View File
@@ -301,16 +301,16 @@ export class TransactionExplorerQuery {
switch (field) {
case TransactionExplorerConditionField.TransactionType:
condition = new TransactionExplorerTransactionTypeCondition([ TransactionType.Expense, TransactionType.Income, TransactionType.Transfer ]);
condition = new TransactionExplorerTransactionTypeCondition(TransactionExplorerConditionOperatorType.In, [ TransactionType.Expense, TransactionType.Income, TransactionType.Transfer ]);
break;
case TransactionExplorerConditionField.TransactionCategory:
condition = new TransactionExplorerTransactionCategoryCondition([]);
condition = new TransactionExplorerTransactionCategoryCondition(TransactionExplorerConditionOperatorType.In, []);
break;
case TransactionExplorerConditionField.SourceAccount:
condition = new TransactionExplorerSourceAccountCondition([]);
condition = new TransactionExplorerSourceAccountCondition(TransactionExplorerConditionOperatorType.In, []);
break;
case TransactionExplorerConditionField.DestinationAccount:
condition = new TransactionExplorerDestinationAccountCondition([]);
condition = new TransactionExplorerDestinationAccountCondition(TransactionExplorerConditionOperatorType.In, []);
break;
case TransactionExplorerConditionField.SourceAmount:
condition = new TransactionExplorerSourceAmountCondition(TransactionExplorerConditionOperatorType.Between, [0, 0]);
@@ -331,7 +331,7 @@ export class TransactionExplorerQuery {
condition = new TransactionExplorerDescriptionCondition(TransactionExplorerConditionOperatorType.Contains, '');
break;
default:
condition = new TransactionExplorerTransactionTypeCondition([ TransactionType.Expense, TransactionType.Income, TransactionType.Transfer ]);
condition = new TransactionExplorerTransactionTypeCondition(TransactionExplorerConditionOperatorType.In, [ TransactionType.Expense, TransactionType.Income, TransactionType.Transfer ]);
break;
}
@@ -737,23 +737,23 @@ export class TransactionExplorerConditionWithRelation {
switch (conditionField) {
case TransactionExplorerConditionField.TransactionType.value:
if (conditionOperator === TransactionExplorerConditionOperatorType.In && Array.isArray(conditionValue)) {
condition = new TransactionExplorerTransactionTypeCondition(conditionValue as number[]);
if (TransactionExplorerTransactionTypeCondition.supportedOperators[conditionOperator] && Array.isArray(conditionValue)) {
condition = new TransactionExplorerTransactionTypeCondition(conditionOperator as TransactionTypeConditionOperator, conditionValue as number[]);
}
break;
case TransactionExplorerConditionField.TransactionCategory.value:
if (conditionOperator === TransactionExplorerConditionOperatorType.In && Array.isArray(conditionValue)) {
condition = new TransactionExplorerTransactionCategoryCondition(conditionValue as string[]);
if (TransactionExplorerTransactionCategoryCondition.supportedOperators[conditionOperator] && Array.isArray(conditionValue)) {
condition = new TransactionExplorerTransactionCategoryCondition(conditionOperator as TransactionCategoryConditionOperator, conditionValue as string[]);
}
break;
case TransactionExplorerConditionField.SourceAccount.value:
if (conditionOperator === TransactionExplorerConditionOperatorType.In && Array.isArray(conditionValue)) {
condition = new TransactionExplorerSourceAccountCondition(conditionValue as string[]);
if (TransactionExplorerSourceAccountCondition.supportedOperators[conditionOperator] && Array.isArray(conditionValue)) {
condition = new TransactionExplorerSourceAccountCondition(conditionOperator as AccountConditionOperator, conditionValue as string[]);
}
break;
case TransactionExplorerConditionField.DestinationAccount.value:
if (conditionOperator === TransactionExplorerConditionOperatorType.In && Array.isArray(conditionValue)) {
condition = new TransactionExplorerDestinationAccountCondition(conditionValue as string[]);
if (TransactionExplorerDestinationAccountCondition.supportedOperators[conditionOperator] && Array.isArray(conditionValue)) {
condition = new TransactionExplorerDestinationAccountCondition(conditionOperator as AccountConditionOperator, conditionValue as string[]);
}
break;
case TransactionExplorerConditionField.SourceAmount.value:
@@ -831,15 +831,20 @@ export class TransactionExplorerUndefinedCondition implements TransactionExplore
}
}
type TransactionTypeConditionOperator = TransactionExplorerConditionOperatorType.In |
TransactionExplorerConditionOperatorType.NotIn;
export class TransactionExplorerTransactionTypeCondition implements TransactionExplorerCondition<TransactionExplorerConditionFieldType.TransactionType, number[]> {
public static readonly supportedOperators: PartialRecord<TransactionExplorerConditionOperatorType, true> = {
[TransactionExplorerConditionOperatorType.In]: true
[TransactionExplorerConditionOperatorType.In]: true,
[TransactionExplorerConditionOperatorType.NotIn]: true
};
public readonly field = TransactionExplorerConditionFieldType.TransactionType;
public readonly operator: TransactionExplorerConditionOperatorType.In = TransactionExplorerConditionOperatorType.In;
public readonly operator: TransactionTypeConditionOperator = TransactionExplorerConditionOperatorType.In;
public value: number[];
constructor(value: number[]) {
constructor(operator: TransactionTypeConditionOperator, value: number[]) {
this.operator = operator;
this.value = value;
}
@@ -848,7 +853,13 @@ export class TransactionExplorerTransactionTypeCondition implements TransactionE
}
public match(transaction: TransactionInsightDataItem): boolean {
return this.value.includes(transaction.type);
if (this.operator === TransactionExplorerConditionOperatorType.In) {
return this.value.includes(transaction.type);
} else if (this.operator === TransactionExplorerConditionOperatorType.NotIn) {
return !this.value.includes(transaction.type);
}
return false;
}
public toExpression(): string {
@@ -863,19 +874,31 @@ export class TransactionExplorerTransactionTypeCondition implements TransactionE
return type.toString();
}
}).join(', ');
return `type IN (${textualTypes})`;
if (this.operator === TransactionExplorerConditionOperatorType.In) {
return `type IN (${textualTypes})`;
} else if (this.operator === TransactionExplorerConditionOperatorType.NotIn) {
return `type NOT IN (${textualTypes})`;
} else {
return '';
}
}
}
type TransactionCategoryConditionOperator = TransactionExplorerConditionOperatorType.In |
TransactionExplorerConditionOperatorType.NotIn;
export class TransactionExplorerTransactionCategoryCondition implements TransactionExplorerCondition<TransactionExplorerConditionFieldType.TransactionCategory, string[]> {
public static readonly supportedOperators: PartialRecord<TransactionExplorerConditionOperatorType, true> = {
[TransactionExplorerConditionOperatorType.In]: true
[TransactionExplorerConditionOperatorType.In]: true,
[TransactionExplorerConditionOperatorType.NotIn]: true
};
public readonly field = TransactionExplorerConditionFieldType.TransactionCategory;
public readonly operator: TransactionExplorerConditionOperatorType.In = TransactionExplorerConditionOperatorType.In;
public readonly operator: TransactionCategoryConditionOperator = TransactionExplorerConditionOperatorType.In;
public value: string[];
constructor(value: string[]) {
constructor(operator: TransactionCategoryConditionOperator, value: string[]) {
this.operator = operator
this.value = value;
}
@@ -884,7 +907,13 @@ export class TransactionExplorerTransactionCategoryCondition implements Transact
}
public match(transaction: TransactionInsightDataItem): boolean {
return this.value.includes(transaction.primaryCategory?.id ?? '') || this.value.includes(transaction.secondaryCategory?.id ?? transaction.categoryId);
if (this.operator === TransactionExplorerConditionOperatorType.In) {
return this.value.includes(transaction.primaryCategory?.id ?? '') || this.value.includes(transaction.secondaryCategory?.id ?? transaction.categoryId);
} else if (this.operator === TransactionExplorerConditionOperatorType.NotIn) {
return !this.value.includes(transaction.primaryCategory?.id ?? '') && !this.value.includes(transaction.secondaryCategory?.id ?? transaction.categoryId);
}
return false;
}
public toExpression(allCategoriesMap: Record<string, TransactionCategory>): string {
@@ -901,19 +930,31 @@ export class TransactionExplorerTransactionCategoryCondition implements Transact
return `'${id}'`;
}
}).filter(item => !!item).join(', ');
return `category IN (${textualCategories})`;
if (this.operator === TransactionExplorerConditionOperatorType.In) {
return `category IN (${textualCategories})`;
} else if (this.operator === TransactionExplorerConditionOperatorType.NotIn) {
return `category NOT IN (${textualCategories})`;
} else {
return '';
}
}
}
type AccountConditionOperator = TransactionExplorerConditionOperatorType.In |
TransactionExplorerConditionOperatorType.NotIn;
export class TransactionExplorerSourceAccountCondition implements TransactionExplorerCondition<TransactionExplorerConditionFieldType.SourceAccount, string[]> {
public static readonly supportedOperators: PartialRecord<TransactionExplorerConditionOperatorType, true> = {
[TransactionExplorerConditionOperatorType.In]: true
[TransactionExplorerConditionOperatorType.In]: true,
[TransactionExplorerConditionOperatorType.NotIn]: true
};
public readonly field = TransactionExplorerConditionFieldType.SourceAccount;
public readonly operator: TransactionExplorerConditionOperatorType.In = TransactionExplorerConditionOperatorType.In;
public readonly operator: AccountConditionOperator = TransactionExplorerConditionOperatorType.In;
public value: string[];
constructor(value: string[]) {
constructor(operator: AccountConditionOperator, value: string[]) {
this.operator = operator;
this.value = value;
}
@@ -922,7 +963,13 @@ export class TransactionExplorerSourceAccountCondition implements TransactionExp
}
public match(transaction: TransactionInsightDataItem): boolean {
return this.value.includes(transaction.sourceAccountId);
if (this.operator === TransactionExplorerConditionOperatorType.In) {
return this.value.includes(transaction.sourceAccountId);
} else if (this.operator === TransactionExplorerConditionOperatorType.NotIn) {
return !this.value.includes(transaction.sourceAccountId);
}
return false;
}
public toExpression(allCategoriesMap: Record<string, TransactionCategory>, allAccountsMap: Record<string, Account>): string {
@@ -939,19 +986,28 @@ export class TransactionExplorerSourceAccountCondition implements TransactionExp
return `'${id}'`;
}
}).filter(item => !!item).join(', ');
return `source_account IN (${textualAccounts})`;
if (this.operator === TransactionExplorerConditionOperatorType.In) {
return `source_account IN (${textualAccounts})`;
} else if (this.operator === TransactionExplorerConditionOperatorType.NotIn) {
return `source_account NOT IN (${textualAccounts})`;
} else {
return '';
}
}
}
export class TransactionExplorerDestinationAccountCondition implements TransactionExplorerCondition<TransactionExplorerConditionFieldType.DestinationAccount, string[]> {
public static readonly supportedOperators: PartialRecord<TransactionExplorerConditionOperatorType, true> = {
[TransactionExplorerConditionOperatorType.In]: true
[TransactionExplorerConditionOperatorType.In]: true,
[TransactionExplorerConditionOperatorType.NotIn]: true
};
public readonly field = TransactionExplorerConditionFieldType.DestinationAccount;
public readonly operator: TransactionExplorerConditionOperatorType.In = TransactionExplorerConditionOperatorType.In;
public readonly operator: AccountConditionOperator = TransactionExplorerConditionOperatorType.In;
public value: string[];
constructor(value: string[]) {
constructor(operator: AccountConditionOperator, value: string[]) {
this.operator = operator;
this.value = value;
}
@@ -960,7 +1016,13 @@ export class TransactionExplorerDestinationAccountCondition implements Transacti
}
public match(transaction: TransactionInsightDataItem): boolean {
return this.value.includes(transaction.destinationAccountId);
if (this.operator === TransactionExplorerConditionOperatorType.In) {
return this.value.includes(transaction.destinationAccountId);
} else if (this.operator === TransactionExplorerConditionOperatorType.NotIn) {
return !this.value.includes(transaction.destinationAccountId);
}
return false;
}
public toExpression(allCategoriesMap: Record<string, TransactionCategory>, allAccountsMap: Record<string, Account>): string {
@@ -977,7 +1039,14 @@ export class TransactionExplorerDestinationAccountCondition implements Transacti
return `'${id}'`;
}
}).filter(item => !!item).join(', ');
return `destination_account IN (${textualAccounts})`;
if (this.operator === TransactionExplorerConditionOperatorType.In) {
return `destination_account IN (${textualAccounts})`;
} else if (this.operator === TransactionExplorerConditionOperatorType.NotIn) {
return `destination_account NOT IN (${textualAccounts})`;
} else {
return '';
}
}
}