migrate transaction store to composition API and typescript

This commit is contained in:
MaysWind
2025-01-28 11:58:42 +08:00
parent 50c3fee7dc
commit 782bc11950
25 changed files with 1948 additions and 1496 deletions
+104 -1
View File
@@ -1,4 +1,107 @@
import type { TransactionGeoLocationResponse } from './transaction.ts';
import { TransactionType } from '@/core/transaction.ts';
import type { TransactionCreateRequest, TransactionGeoLocationResponse } from './transaction.ts';
export class ImportTransaction implements ImportTransactionResponse {
public type: number;
public categoryId: string;
public originalCategoryName: string;
public time: number;
public utcOffset: number;
public sourceAccountId: string;
public originalSourceAccountName: string;
public originalSourceAccountCurrency: string;
public destinationAccountId: string;
public originalDestinationAccountName?: string;
public originalDestinationAccountCurrency?: string;
public sourceAmount: number;
public destinationAmount: number;
public tagIds: string[];
public originalTagNames: string[];
public comment: string;
public geoLocation?: TransactionGeoLocationResponse;
public actualCategoryName: string;
public actualSourceAccountName: string;
public actualDestinationAccountName?: string;
public index: number;
public selected: boolean;
public valid: boolean;
private constructor(response: ImportTransactionResponse, index: number) {
this.type = response.type;
this.categoryId = response.categoryId;
this.originalCategoryName = response.originalCategoryName;
this.time = response.time;
this.utcOffset = response.utcOffset;
this.sourceAccountId = response.sourceAccountId;
this.originalSourceAccountName = response.originalSourceAccountName;
this.originalSourceAccountCurrency = response.originalSourceAccountCurrency;
this.destinationAccountId = response.destinationAccountId || '';
this.originalDestinationAccountName = response.originalDestinationAccountName;
this.originalDestinationAccountCurrency = response.originalDestinationAccountCurrency;
this.sourceAmount = response.sourceAmount;
this.destinationAmount = response.destinationAmount || 0;
this.tagIds = response.tagIds;
this.originalTagNames = response.originalTagNames;
this.comment = response.comment;
this.geoLocation = response.geoLocation;
this.actualCategoryName = response.originalCategoryName;
this.actualSourceAccountName = response.originalSourceAccountName;
this.actualDestinationAccountName = response.originalDestinationAccountName;
this.index = index;
this.selected = false;
this.valid = this.isTransactionValid();
}
public toCreateRequest(): TransactionCreateRequest {
return {
type: this.type,
categoryId: this.categoryId,
time: this.time,
utcOffset: this.utcOffset,
sourceAccountId: this.sourceAccountId,
destinationAccountId: this.type === TransactionType.Transfer ? this.destinationAccountId : '0',
sourceAmount: this.sourceAmount,
destinationAmount: this.type === TransactionType.Transfer ? this.destinationAmount : 0,
hideAmount: false,
tagIds: this.tagIds,
pictureIds: [],
comment: this.comment,
geoLocation: this.geoLocation,
clientSessionId: ''
};
}
public isTransactionValid(): boolean {
if (this.type !== TransactionType.ModifyBalance && (!this.categoryId || this.categoryId === '0')) {
return false;
}
if (!this.sourceAccountId || this.sourceAccountId === '0') {
return false;
}
if (this.type === TransactionType.Transfer && (!this.destinationAccountId || this.destinationAccountId === '0')) {
return false;
}
if (this.tagIds && this.tagIds.length) {
for (const tagId of this.tagIds) {
if (!tagId || tagId === '0') {
return false;
}
}
}
return true;
}
public static of(response: ImportTransactionResponse, index: number): ImportTransaction {
return new ImportTransaction(response, index);
}
}
export interface ImportTransactionResponse {
readonly type: number;
+454 -4
View File
@@ -1,10 +1,450 @@
import type { PartialRecord } from '@/core/base.ts';
import type { YearMonth, StartEndTime } from '@/core/datetime.ts';
import { TransactionType } from '@/core/transaction.ts';
import type { AccountInfoResponse } from './account.ts';
import type { TransactionCategoryInfoResponse } from './transaction_category.ts';
import { Account, type AccountInfoResponse } from './account.ts';
import { TransactionCategory, type TransactionCategoryInfoResponse } from './transaction_category.ts';
import { TransactionTag, type TransactionTagInfoResponse } from './transaction_tag.ts';
import type { TransactionPictureInfoBasicResponse } from './transaction_picture_info.ts';
import type { TransactionTagInfoResponse } from './transaction_tag.ts';
export class Transaction implements TransactionInfoResponse {
public id: string;
public timeSequenceId: string;
public type: number;
public expenseCategoryId: string = '';
public incomeCategoryId: string = '';
public transferCategoryId: string = '';
public time: number;
public timeZone?: string; // only in new transaction
public utcOffset: number;
public sourceAccountId: string;
public destinationAccountId: string;
public sourceAmount: number;
public destinationAmount: number;
public hideAmount: boolean;
public tagIds: string[];
public comment: string;
public editable: boolean;
private _pictures?: TransactionPicture[];
private _geoLocation?: TransactionGeoLocation;
private _category?: TransactionCategory; // only for displaying transaction
private _sourceAccount?: Account; // only for displaying transaction
private _destinationAccount?: Account; // only for displaying transaction
private _tags?: TransactionTag[]; // only for displaying transaction
private _date?: string = undefined; // only for displaying transaction in transaction list
private _day?: number = undefined; // only for displaying transaction in transaction list
private _dayOfWeek?: string = undefined; // only for displaying transaction in transaction list
private constructor(id: string, timeSequenceId: string, type: number, categoryId: string, time: number, timeZone: string | undefined, utcOffset: number, sourceAccountId: string, destinationAccountId: string, sourceAmount: number, destinationAmount: number, hideAmount: boolean, tagIds: string[], comment: string, editable: boolean) {
this.id = id;
this.timeSequenceId = timeSequenceId;
this.type = type;
this.time = time;
this.timeZone = timeZone;
this.utcOffset = utcOffset;
this.sourceAccountId = sourceAccountId;
this.destinationAccountId = destinationAccountId;
this.sourceAmount = sourceAmount;
this.destinationAmount = destinationAmount;
this.hideAmount = hideAmount;
this.tagIds = tagIds;
this.comment = comment;
this.editable = editable;
this.setCategoryId(categoryId);
}
public get pictures(): TransactionPictureInfoBasicResponse[] | undefined {
const ret: TransactionPictureInfoBasicResponse[] = [];
if (this._pictures) {
for (const picture of this._pictures) {
ret.push(picture);
}
}
return ret;
}
public get geoLocation(): TransactionGeoLocationResponse | undefined {
return this._geoLocation;
}
public get categoryId(): string {
return this.getCategoryId();
}
public get category(): TransactionCategoryInfoResponse | undefined {
return this._category;
}
public get sourceAccount(): AccountInfoResponse | undefined {
return this._sourceAccount;
}
public get destinationAccount(): AccountInfoResponse | undefined {
return this._destinationAccount;
}
public get tags(): TransactionTagInfoResponse[] | undefined {
const ret: TransactionTagInfoResponse[] = [];
if (this._tags) {
for (const tag of this._tags) {
ret.push(tag);
}
}
return ret;
}
public get date(): string | undefined {
return this._date;
}
public get day(): number | undefined {
return this._day;
}
public get dayOfWeek(): string | undefined {
return this._dayOfWeek;
}
public getCategoryId(): string {
if (this.type === TransactionType.Expense) {
return this.expenseCategoryId;
} else if (this.type === TransactionType.Income) {
return this.incomeCategoryId;
} else if (this.type === TransactionType.Transfer) {
return this.transferCategoryId;
} else {
return '';
}
}
public setCategoryId(categoryId: string): void {
if (this.type === TransactionType.Expense) {
this.expenseCategoryId = categoryId;
} else if (this.type === TransactionType.Income) {
this.incomeCategoryId = categoryId;
} else if (this.type === TransactionType.Transfer) {
this.transferCategoryId = categoryId;
}
}
public setCategory(category: TransactionCategory): void {
this._category = category;
}
public setSourceAccount(sourceAccount: Account): void {
this._sourceAccount = sourceAccount;
}
public setDestinationAccount(destinationAccount: Account): void {
this._destinationAccount = destinationAccount;
}
public setTags(tags: TransactionTag[]): void {
this._tags = tags;
}
public getPictureIds(): string[] {
const pictureIds: string[] = [];
if (this._pictures) {
for (const picture of this._pictures) {
pictureIds.push(picture.pictureId);
}
}
return pictureIds;
}
public setPictures(pictures: TransactionPicture[]): void {
this._pictures = pictures;
}
public addPicture(pictureInfo: TransactionPictureInfoBasicResponse): void {
if (!this._pictures) {
this._pictures = [];
}
this._pictures.push(TransactionPicture.of(pictureInfo));
}
public removePicture(pictureInfo: TransactionPictureInfoBasicResponse): void {
if (!this._pictures) {
return;
}
for (let i = 0; i < this._pictures.length; i++) {
if (this._pictures[i].pictureId === pictureInfo.pictureId) {
this._pictures.splice(i, 1);
}
}
}
public clearPictures(): void {
this._pictures = [];
}
public setGeoLocation(geoLocation?: TransactionGeoLocation): void {
this._geoLocation = geoLocation;
}
public setLatitudeAndLongitude(latitude: number, longitude: number): void {
this._geoLocation = TransactionGeoLocation.createNewGeoLocation(latitude, longitude);
}
public removeGeoLocation(): void {
this._geoLocation = undefined;
}
public setDisplayDate(date: string, day: number, dayOfWeek: string): void {
this._date = date;
this._day = day;
this._dayOfWeek = dayOfWeek;
}
public toCreateRequest(clientSessionId: string, actualTime?: number): TransactionCreateRequest {
return {
type: this.type,
categoryId: this.getCategoryId(),
time: actualTime ? actualTime : this.time,
utcOffset: this.utcOffset,
sourceAccountId: this.sourceAccountId,
destinationAccountId: this.type === TransactionType.Transfer ? this.destinationAccountId : '0',
sourceAmount: this.sourceAmount,
destinationAmount: this.type === TransactionType.Transfer ? this.destinationAmount : 0,
hideAmount: this.hideAmount,
tagIds: this.tagIds,
pictureIds: this.getPictureIds(),
comment: this.comment,
geoLocation: this.geoLocation,
clientSessionId: clientSessionId
};
}
public toModifyRequest(actualTime?: number): TransactionModifyRequest {
return {
id: this.id,
categoryId: this.getCategoryId(),
time: actualTime ? actualTime : this.time,
utcOffset: this.utcOffset,
sourceAccountId: this.sourceAccountId,
destinationAccountId: this.type === TransactionType.Transfer ? this.destinationAccountId : '0',
sourceAmount: this.sourceAmount,
destinationAmount: this.type === TransactionType.Transfer ? this.destinationAmount : 0,
hideAmount: this.hideAmount,
tagIds: this.tagIds,
pictureIds: this.getPictureIds(),
comment: this.comment,
geoLocation: this.geoLocation
};
}
public toTransactionDraft(): TransactionDraft | null {
if (this.type !== TransactionType.Expense &&
this.type !== TransactionType.Income &&
this.type !== TransactionType.Transfer) {
return null;
}
return {
type: this.type,
categoryId: this.getCategoryId(),
sourceAccountId: this.sourceAccountId,
sourceAmount: this.sourceAmount,
destinationAccountId: this.type === TransactionType.Transfer ? this.destinationAccountId : '0',
destinationAmount: this.type === TransactionType.Transfer ? this.destinationAmount : 0,
hideAmount: this.hideAmount,
tagIds: this.tagIds,
pictures: this.pictures,
comment: this.comment,
};
}
public static createNewTransaction(type: number, time: number, timeZone: string, utcOffset: number): Transaction {
return new Transaction(
'', // id
'', // timeSequenceId
type, // type
'', // categoryId
time, // time
timeZone, // timeZone
utcOffset, // utcOffset
'', // sourceAccountId
'', // destinationAccountId
0, // sourceAmount
0, // destinationAmount
false, // hideAmount
[], // tagIds
'', // comment
true // editable
);
}
public static of(transactionResponse: TransactionInfoResponse): Transaction {
const transaction: Transaction = new Transaction(
transactionResponse.id,
transactionResponse.timeSequenceId,
transactionResponse.type,
transactionResponse.categoryId,
transactionResponse.time,
undefined, // only in new transaction
transactionResponse.utcOffset,
transactionResponse.sourceAccountId,
transactionResponse.destinationAccountId,
transactionResponse.sourceAmount,
transactionResponse.destinationAmount,
transactionResponse.hideAmount,
transactionResponse.tagIds,
transactionResponse.comment,
transactionResponse.editable
);
if (transactionResponse.category) {
transaction.setCategory(TransactionCategory.of(transactionResponse.category));
}
if (transactionResponse.sourceAccount) {
transaction.setSourceAccount(Account.of(transactionResponse.sourceAccount));
}
if (transactionResponse.destinationAccount) {
transaction.setDestinationAccount(Account.of(transactionResponse.destinationAccount));
}
if (transactionResponse.tags) {
transaction.setTags(TransactionTag.ofMany(transactionResponse.tags));
}
if (transactionResponse.pictures) {
const pictures: TransactionPicture[] = [];
for (const picture of transactionResponse.pictures) {
pictures.push(TransactionPicture.of(picture));
}
transaction.setPictures(pictures);
}
if (transactionResponse.geoLocation) {
transaction.setLatitudeAndLongitude(transactionResponse.geoLocation.latitude, transactionResponse.geoLocation.longitude);
}
return transaction;
}
public static ofMany(transactionResponses: TransactionInfoResponse[]): Transaction[] {
const transactions: Transaction[] = [];
for (const transactionResponse of transactionResponses) {
transactions.push(Transaction.of(transactionResponse));
}
return transactions;
}
public static ofDraft(transactionDraft?: TransactionDraft | null): Transaction | null {
if (!transactionDraft) {
return null;
}
if (transactionDraft.type !== TransactionType.Expense &&
transactionDraft.type !== TransactionType.Income &&
transactionDraft.type !== TransactionType.Transfer) {
return null;
}
const transaction: Transaction = new Transaction(
'', // id
'', // timeSequenceId
transactionDraft.type, // type
transactionDraft.categoryId ?? '', // categoryId
0, // time
undefined, // only in new transaction
0, // utcOffset
transactionDraft.sourceAccountId ?? '', // sourceAccountId
transactionDraft.destinationAccountId ?? '', // destinationAccountId
transactionDraft.sourceAmount ?? 0, // sourceAmount
transactionDraft.destinationAmount ?? 0, // destinationAmount
transactionDraft.hideAmount ?? false, // hideAmount
transactionDraft.tagIds ?? [], // tagIds
transactionDraft.comment ?? '', // comment
true // editable
);
if (transactionDraft.pictures) {
const pictures: TransactionPicture[] = [];
for (const picture of transactionDraft.pictures) {
pictures.push(TransactionPicture.of(picture));
}
transaction.setPictures(pictures);
}
return transaction;
}
}
export class TransactionPicture implements TransactionPictureInfoBasicResponse {
public pictureId: string;
public originalUrl: string;
private constructor(pictureId: string, originalUrl: string) {
this.pictureId = pictureId;
this.originalUrl = originalUrl;
}
public static of(picture: TransactionPictureInfoBasicResponse): TransactionPicture {
return new TransactionPicture(picture.pictureId, picture.originalUrl);
}
public static ofMany(pictureResponses: TransactionPictureInfoBasicResponse[]): TransactionPicture[] {
const pictures: TransactionPicture[] = [];
for (const pictureResponse of pictureResponses) {
pictures.push(TransactionPicture.of(pictureResponse));
}
return pictures;
}
}
export class TransactionGeoLocation implements TransactionGeoLocationRequest {
public latitude: number;
public longitude: number;
private constructor(latitude: number, longitude: number) {
this.latitude = latitude;
this.longitude = longitude;
}
public static createNewGeoLocation(latitude: number, longitude: number): TransactionGeoLocation {
return new TransactionGeoLocation(latitude, longitude);
}
public static of(geoLocation: TransactionGeoLocationRequest): TransactionGeoLocation {
return new TransactionGeoLocation(geoLocation.latitude, geoLocation.longitude);
}
}
export interface TransactionDraft {
readonly type?: number;
readonly categoryId?: string;
readonly sourceAccountId?: string;
readonly sourceAmount?: number;
readonly destinationAccountId?: string;
readonly destinationAmount?: number;
readonly hideAmount?: boolean;
readonly tagIds?: string[];
readonly pictures?: TransactionPictureInfoBasicResponse[];
readonly comment?: string;
}
export interface TransactionGeoLocationRequest {
readonly latitude: number;
@@ -209,7 +649,7 @@ export class TransactionAmountsRequest {
export interface TransactionInfoPageWrapperResponse {
readonly items: TransactionInfoResponse[];
readonly nextTimeSequenceId?: string;
readonly nextTimeSequenceId?: number;
readonly totalCount?: number;
}
@@ -218,6 +658,11 @@ export interface TransactionInfoPageWrapperResponse2 {
readonly totalCount: number;
}
export interface TransactionPageWrapper {
readonly items: Transaction[];
readonly totalCount?: number;
}
export interface TransactionStatisticResponse {
readonly startTime: number;
readonly endTime: number;
@@ -344,3 +789,8 @@ export interface TransactionMonthlyIncomeAndExpenseData {
readonly incompleteIncomeAmount: boolean;
readonly incompleteExpenseAmount: boolean;
}
export const EMPTY_TRANSACTION_RESULT: TransactionPageWrapper = {
items: [],
totalCount: 0
}