Personal Expense Management Application using Ionic 4

Hello Viewers,
In this tutorial, we are going to see Personal Expense Management Application using Ionic 4, where the database will be SQLite.

Let's start with this project.

Install node.js in your system
Go to link - https://nodejs.org/en/ and install the latest version of ndoe.js
After installing,
   Check npm is working properly using the Command Prompt -

   C:\Users\admin>npm -version
   6.10.2


Install Ionic in your system
Open Command Prompt and run below command.

  npm install -g ionic

Run below command before going ahead.

  ionic start sqlliteApp sidemenu
  cd sqlliteApp
  ionic g service services/database
  ionic g page pages/credit
  npm install @ionic-native/sqlite @ionic-native/sqlite-porter
  ionic cordova plugin add cordova-sqlite-storage
  ionic cordova plugin add uk.co.workingedge.cordova.plugin.sqliteporter

You need a SQL script file to create a database and table.
Open your application in file explorer and create inputCredit.sql in assets directory as below.
  sqlLite\src\assets\inputCredit.sql

Update sqlLite\src\assets\inputCredit.sql file with below details:
CREATE TABLE IF NOT EXISTS Credit_Details(id INTEGER PRIMARY KEY AUTOINCREMENT,card_name TEXT,bank_name TEXT,expensed TEXT, left_money TEXT , card_limit TEXT);
CREATE TABLE IF NOT EXISTS Credit_Expense_Transaction(id INTEGER PRIMARY KEY AUTOINCREMENT,transaction_details TEXT,date_of_transaction TEXT,amount TEXT,card_id INTEGER);

Update the sqlLite\src\app\app.module.ts file with the below entries.
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { RouteReuseStrategy } from '@angular/router';

import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { SQLitePorter } from '@ionic-native/sqlite-porter/ngx';
import { SQLite } from '@ionic-native/sqlite/ngx';

import { HttpClientModule } from '@angular/common/http';

@NgModule({
  declarations: [AppComponent],
  entryComponents: [],
  imports: [
    BrowserModule,
    IonicModule.forRoot(),
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [
    StatusBar,
    SplashScreen,
    { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
    SQLite,
    SQLitePorter
  ],
  bootstrap: [AppComponent]
})
export class AppModule {}

Update the sqlLite\src\app\app.component.ts file with the below entries.
import { Component } from '@angular/core';

import { Platform } from '@ionic/angular';
import { SplashScreen } from '@ionic-native/splash-screen/ngx';
import { StatusBar } from '@ionic-native/status-bar/ngx';

@Component({
  selector: 'app-root',
  templateUrl: 'app.component.html',
  styleUrls: ['app.component.scss']
})
export class AppComponent {
  public appPages = [
    {
      title: 'Transactions',
      url: '/credit',
      icon: 'home'
    },
    {
      title: 'Reset',
      url: '/home',
      icon: 'aperture'
    }
  ];

  constructor(
    private platform: Platform,
    private splashScreen: SplashScreen,
    private statusBar: StatusBar
  ) {
    this.initializeApp();
  }

  initializeApp() {
    this.platform.ready().then(() => {
      this.statusBar.styleDefault();
      this.splashScreen.hide();
    });
  }
}

Update the sqlLite\src\app\app-routing.module.ts file with the below entries.
import { NgModule } from '@angular/core';
import { PreloadAllModules, RouterModule, Routes } from '@angular/router';

const routes: Routes = [
  {
    path: '',
    redirectTo: 'credit',
    pathMatch: 'full'
  },
  {
    path: 'home',
    loadChildren: () => import('./home/home.module').then(m => m.HomePageModule)
  },
  { path: 'credit',
    loadChildren: () => import ('./pages/credit/credit.module').then(m => m.CreditPageModule) }
];

@NgModule({
  imports: [
    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
  ],
  exports: [RouterModule]
})
export class AppRoutingModule {}

Update the sqlLite\src\app\services\database.service.ts file with the below entries.
import { Platform } from '@ionic/angular';
import { Injectable } from '@angular/core';
import { SQLitePorter } from '@ionic-native/sqlite-porter/ngx';
import { HttpClient } from '@angular/common/http';
import { SQLite, SQLiteObject } from '@ionic-native/sqlite/ngx';
import { BehaviorSubject, Observable } from 'rxjs';

export interface CreditData {
  id: number;
  card_name: string;
  bank_name: string;
  expensed: string;
  left_money: string;
  card_limit: string;
}

@Injectable({
  providedIn: 'root'
})
export class DatabaseService {
  private database: SQLiteObject;
  private dbReady: BehaviorSubject<boolean> = new BehaviorSubject(false);
  creditsData = new BehaviorSubject([]);
  creditTransactions = new BehaviorSubject([]);
  expensedMoney: any ;
  leftMoney: any ;
  actualLimit: any;
  constructor(private plt: Platform, private sqlitePorter: SQLitePorter, private sqlite: SQLite, private http: HttpClient) {
    this.plt.ready().then(() => {
      this.sqlite.create({
        name: 'Credit.db',
        location: 'default'
      })
      .then((db: SQLiteObject) => {
          this.database = db;
          this.seedDatabase();
      });
    });
  }
  seedDatabase() {
    this.http.get('assets/inputCredit.sql', { responseType: 'text'})
    .subscribe(sql => {
      this.sqlitePorter.importSqlToDb(this.database, sql)
        .then(_ => {
          this.loadCreditDetails();
          this.loadCreditTransaction();
          this.dbReady.next(true);
        })
        .catch(e => console.error(e));
    });
  }
  getDatabaseState() {
    return this.dbReady.asObservable();
  }
  getCreditData(): Observable<CreditData[]> {
    return this.creditsData.asObservable();
  }
  getCreditTransactions(): Observable<any[]> {
    return this.creditTransactions.asObservable();
  }
  loadCreditDetails() {
    return this.database.executeSql('SELECT * FROM Credit_Details', []).then(data => {
      let creditsData: CreditData[] = [];
      console.log('loading data for Credit Card');
      if (data.rows.length > 0) {
        for (let i = 0; i < data.rows.length; i++) {
          creditsData.push({
            id: data.rows.item(i).id,
            card_name: data.rows.item(i).card_name,
            bank_name: data.rows.item(i).bank_name,
            expensed: data.rows.item(i).expensed,
            left_money: data.rows.item(i).left_money,
            card_limit: data.rows.item(i).card_limit
           });
        }
      }
      this.creditsData.next(creditsData);
    });
  }
  loadCreditTransaction() {
    let query = 'SELECT ct.id, ct.transaction_details, ct.date_of_transaction, ct.amount, cd.card_name, cd.bank_name from Credit_Expense_Transaction ct JOIN Credit_Details cd ON ct.card_id=cd.id';
    return this.database.executeSql(query, []).then(data => {
      let creditTransactions = [];
      this.loadCreditDetails();
      if (data.rows.length > 0) {
        for (let i = 0; i < data.rows.length; i++) {
          creditTransactions.push({
            id: data.rows.item(i).id,
            transaction_details: data.rows.item(i).transaction_details,
            date_of_transaction: data.rows.item(i).date_of_transaction,
            amount: data.rows.item(i).amount,
            card_name: data.rows.item(i).bank_name + ' ' + data.rows.item(i).card_name
           });
        }
      }
      this.creditTransactions.next(creditTransactions);
    });
  }
  addCreditDetail(cardName, bankName, expensed, leftMoney, cardLimit) {
    let data = [cardName, bankName, expensed, leftMoney, cardLimit];
    return this.database.executeSql('INSERT INTO Credit_Details (card_name,bank_name,expensed, left_money, card_limit) VALUES (?, ?, ?, ?, ?)', data).then(data => {     
      this.loadCreditDetails();
    });
  }
  addCreditTransactions(transactionDetails, dateOfTransaction, amount, cardId) {
    let data = [transactionDetails, dateOfTransaction, amount, cardId];
    return this.database.executeSql('INSERT INTO Credit_Expense_Transaction (transaction_details, date_of_transaction, amount, card_id) VALUES (?, ?, ?, ?)', data).then(data => {
      this.updateCreditData(amount, cardId);
    });
  }
  updateCreditData(amount, cardId) {
    this.leftMoney = Number.parseInt(this.leftMoney.toString()) - Number.parseInt(amount.toString());
    this.expensedMoney = Number.parseInt(this.expensedMoney.toString()) + Number.parseInt(amount.toString());
    let data = [this.expensedMoney, this.leftMoney];
    return this.database.executeSql(`UPDATE Credit_Details SET expensed = ?, left_money = ? WHERE id = ${cardId}`, data).then(data => {
      this.loadCreditTransaction();
    });
  }
  getExpensedAndLeftMoney(cardId) {
    return this.database.executeSql('SELECT * FROM Credit_Details WHERE id = ?', [cardId]).then(data => {
      this.expensedMoney = data.rows.item(0).expensed;
      this.leftMoney = data.rows.item(0).left_money;
    });
  }

  // Adding logic for reseting limit

  getLimitbyId(id) {
    return this.database.executeSql('SELECT * FROM Credit_Details WHERE id = ?', [id]).then(data => {
      console.log('Data inside select ' + data);
      this.actualLimit = data.rows.item(0).card_limit;
    });
  }

  resetCreditLimit(id) {
    this.actualLimit = Number.parseInt(this.actualLimit.toString());
    let data = [0, this.actualLimit];
    return this.database.executeSql(`UPDATE Credit_Details SET expensed = ?, left_money = ? WHERE id = ${id}`, data).then(data => {
      this.loadCreditTransaction();
    });
  }
}

Update the sqlLite\src\app\home\home.page.html file with the below entries.
<ion-header>
  <ion-toolbar color="primary">
    <ion-buttons slot="start">
      <ion-menu-button></ion-menu-button>
    </ion-buttons>
    <ion-title>
      Reset Card Balance
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
    <ion-list>
      <ion-item *ngFor="let cd of creditData | async">
        <ion-label class="ion-text-wrap">
          <h2>{{ cd.bank_name }} {{ cd.card_name }} with Remaining Limit {{ cd.left_money }}</h2>
        </ion-label>
        <ion-button expand="block" (click)="resetLimit(cd.id)">Reset Card</ion-button>
      </ion-item>
      </ion-list>
</ion-content>

Update the sqlLite\src\app\home\home.page.ts file with the below entries.
import { DatabaseService} from './../services/database.service';
import { Component, OnInit } from '@angular/core';
import { ToastController} from '@ionic/angular';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-home',
  templateUrl: 'home.page.html',
  styleUrls: ['home.page.scss'],
})
export class HomePage implements OnInit {
  creditData: Observable<any[]>;
  transHistory: Observable<any[]>;

  transferData = {};
  constructor(private db: DatabaseService, private toast: ToastController) { }
  ngOnInit() {
    this.db.getDatabaseState().subscribe(rdy => {
      if (rdy) {
          this.creditData = this.db.getCreditData();
      }
    });
  }

  resetLimit(id: any) {
    this.db.getLimitbyId(id).then( _ => {
      this.db.resetCreditLimit(id);
    }).then(async (res) => {
      let toast = await this.toast.create({
        message: 'Card details reseted successfully.',
        duration: 3000
      });
      toast.present();
    });
  }
}

Update the sqlLite\src\app\pages\credit\credit.page.html file with the below entries.
<ion-header>
  <ion-toolbar color="primary">
    <ion-buttons slot="start">
      <ion-menu-button></ion-menu-button>
    </ion-buttons>
    <ion-title>
      Credit Transactions
    </ion-title>
  </ion-toolbar>
</ion-header>

<ion-content class="ion-padding">
 
  <ion-segment [(ngModel)]="selectedView">
    <ion-segment-button value="creditDetails">
      <ion-label>Dashboard</ion-label>
    </ion-segment-button>
    <ion-segment-button value="creditTrans">
      <ion-label>Transactions</ion-label>
    </ion-segment-button>
  </ion-segment>
 
  <div [ngSwitch]="selectedView">
    <div *ngSwitchCase="'creditDetails'">
      <ion-item>
        <ion-label position="stacked">Bank Name</ion-label>
        <ion-input [(ngModel)]="indCreditData.bankName" placeholder="Your Card Bank"></ion-input>
      </ion-item>
      <ion-item>
        <ion-label position="stacked">Card Name</ion-label>
        <ion-input [(ngModel)]="indCreditData.cardName" placeholder="Your Card Name"></ion-input>
      </ion-item>
      <ion-item>
        <ion-label position="stacked">Card Initial Limit</ion-label>
        <ion-input [(ngModel)]="indCreditData.cardLimit" placeholder="Card Limit"></ion-input>
      </ion-item>

      <ion-button expand="block" (click)="addCardDetails()">Add Card</ion-button>
 
      <ion-list>
        <ion-item button *ngFor="let cd of creditsData" [routerLink]="['/', 'developers', cd.id]">
          <ion-label class="ion-text-wrap">
            <h2>{{ cd.bank_name }} {{ cd.card_name }} with Remaining Limit {{ cd.left_money }}</h2>
            <p>Expensed Money :: {{ cd.expensed }}</p>
            <p>Actual Limit :: {{ cd.card_limit }}</p>            
          </ion-label>
        </ion-item>
      </ion-list>
    </div>
 
    <div *ngSwitchCase="'creditTrans'">
      <ion-item>
        <ion-label position="stacked">Transactions Details</ion-label>
        <ion-input [(ngModel)]="indCreditTrans.transactionDetails" placeholder="Details"></ion-input>
      </ion-item>
      <ion-item>
        <ion-label position="stacked">Transactions Amount</ion-label>
        <ion-input [(ngModel)]="indCreditTrans.amount" placeholder="Amount"></ion-input>
      </ion-item>
      <ion-item>
        <ion-label position="stacked">Transactions Date</ion-label>
        <ion-input [(ngModel)]="indCreditTrans.dateOfTransaction" placeholder="Date for Transaction"></ion-input>
      </ion-item>
      <ion-item>
        <ion-label position="stacked">Select Card</ion-label>
        <ion-select [(ngModel)]="indCreditTrans.cardId">
          <ion-select-option *ngFor="let cd of creditsData" [value]="cd.id">{{ cd.bank_name }} {{ cd.card_name }}</ion-select-option>
        </ion-select>
      </ion-item>
      <ion-button expand="block" (click)="addCreditTrans()">Add Transaction</ion-button>
 
      <ion-list>
        <ion-item *ngFor="let ct of creditTrans | async">
          <ion-label class="ion-text-wrap">
            <h2>Amount : {{ ct.amount }} from {{ ct.card_name }}</h2>
            <p>Reason : {{ ct.transaction_details }} on {{ ct.date_of_transaction }}</p>
          </ion-label>
        </ion-item>
      </ion-list>
    </div>
  </div> 
</ion-content>

Update the sqlLite\src\app\pages\credit\credit.page.ts file with the below entries.
import { DatabaseService, CreditData } from './../../services/database.service';
import { Component, OnInit } from '@angular/core';
import { ToastController} from '@ionic/angular';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-credit',
  templateUrl: './credit.page.html',
  styleUrls: ['./credit.page.scss'],
})
export class CreditPage implements OnInit {
  creditsData: CreditData[] = [];

  creditTrans: Observable<any[]>;

  indCreditData = {};
  indCreditTrans = {};

  selectedView = 'creditDetails';

  constructor(private db: DatabaseService, private toast: ToastController) { }

  ngOnInit() {
    this.db.getDatabaseState().subscribe(rdy => {
      if (rdy) {
        this.db.getCreditData().subscribe(cd => {
          this.creditsData = cd;
        });
        this.creditTrans = this.db.getCreditTransactions();
      }
    });
  }

  addCardDetails() {
    this.db.addCreditDetail(this.indCreditData['cardName'], this.indCreditData['bankName'], 0, this.indCreditData['cardLimit'], this.indCreditData['cardLimit']).then(_ => {
      this.indCreditData = {};
    }).then(async (res) => {
      let toast = await this.toast.create({
        message: 'Card added successfully.',
        duration: 3000
      });
      toast.present();
    });
  }
  addCreditTrans() {
    this.db.getExpensedAndLeftMoney(this.indCreditTrans['cardId']);
    this.db.addCreditTransactions(this.indCreditTrans['transactionDetails'], this.indCreditTrans['dateOfTransaction'], this.indCreditTrans['amount'], this.indCreditTrans['cardId']).then(_ => {
      this.indCreditTrans = {};
    }).then(async (res) => {
      let toast = await this.toast.create({
        message: 'Transaction details added successfully.',
        duration: 3000
      });
      toast.present();
    });
  }
}

Run below command to make apk file, and run in your mobile.
ionic cordova platform add android
ionic cordova build android

So now you just need to run this code and you will get your required output, please let me know in case of any issue.

Youtube video link for your reference:
Part 1:


Part 2:


Part 3:

Comments

Post a Comment

Popular posts from this blog

How to read XLS and XLSX Excel files in Java

How to Read CSV File in Java

User Login and Logout with the session