From ca60bafddb9d76dc464cb797b0e0ca3bb223085e Mon Sep 17 00:00:00 2001 From: comhendrik Date: Tue, 31 May 2022 23:09:33 +0200 Subject: [PATCH 1/9] Initialize MonthlyExpenseCD --- .DS_Store | Bin 6148 -> 6148 bytes Expenso.xcodeproj/project.pbxproj | 4 ++ .../Expenso.xcdatamodel/contents | 12 +++- .../Library/CoreData/MonthlyExpenseCD.swift | 30 ++++++++ .../Screens/AddExpense/AddExpenseView.swift | 8 +++ .../AddExpense/AddExpenseViewModel.swift | 65 ++++++++++++++++++ Expenso/Screens/Expense/ExpenseView.swift | 5 ++ 7 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 Expenso/Library/CoreData/MonthlyExpenseCD.swift diff --git a/.DS_Store b/.DS_Store index 72ad433d67b0d38008b61a610fab7a6d95bfc54b..52169be979cbf4999c6cfb48cb6e5c547917e1e7 100644 GIT binary patch delta 81 zcmZoMXffCz&c@87WjFZ&tCmP^zKcszPJR*t0|SSU<@ps+%10cb5-C_ECLdtqWnxO$ NSZKkxnVsV=KLGW;8Danc delta 81 zcmZoMXffCz&c@6b_G9t`RxOd-d>5CboctsP1_ll%8{4?XPlp_#5-C_ECLdtqWn!GL NvCx8XGdss$egH|)8w>yd diff --git a/Expenso.xcodeproj/project.pbxproj b/Expenso.xcodeproj/project.pbxproj index e1672fd..c238e47 100644 --- a/Expenso.xcodeproj/project.pbxproj +++ b/Expenso.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 24DF38B62846B7BC006CA05F /* MonthlyExpenseCD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24DF38B52846B7BC006CA05F /* MonthlyExpenseCD.swift */; }; 736C720A25CFD89900720DEA /* empty-face.json in Resources */ = {isa = PBXBuildFile; fileRef = 736C720925CFD89900720DEA /* empty-face.json */; }; 736C721B25CFE8E200720DEA /* LottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736C721A25CFE8E200720DEA /* LottieView.swift */; }; 738B1C1825C65DFE0067407B /* ExpensoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738B1C1725C65DFE0067407B /* ExpensoApp.swift */; }; @@ -53,6 +54,7 @@ /* Begin PBXFileReference section */ 1B0E373FB89DD0864398FEE7 /* Pods_Expenso.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Expenso.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 24DF38B52846B7BC006CA05F /* MonthlyExpenseCD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyExpenseCD.swift; sourceTree = ""; }; 2C2D1600DAC62FA04EDCF170 /* Pods-Expenso.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Expenso.debug.xcconfig"; path = "Target Support Files/Pods-Expenso/Pods-Expenso.debug.xcconfig"; sourceTree = ""; }; 736C720925CFD89900720DEA /* empty-face.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "empty-face.json"; sourceTree = ""; }; 736C721A25CFE8E200720DEA /* LottieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieView.swift; sourceTree = ""; }; @@ -199,6 +201,7 @@ isa = PBXGroup; children = ( 738B1C3225C660140067407B /* ExpenseCD.swift */, + 24DF38B52846B7BC006CA05F /* MonthlyExpenseCD.swift */, ); path = CoreData; sourceTree = ""; @@ -456,6 +459,7 @@ 738B1C5225C6632F0067407B /* HelperMethods.swift in Sources */, 738B1C2425C65E060067407B /* Expenso.xcdatamodeld in Sources */, 738B1C8B25C67D880067407B /* ExpenseFilterView.swift in Sources */, + 24DF38B62846B7BC006CA05F /* MonthlyExpenseCD.swift in Sources */, 739DFF7B25DC1E3C005BD5C8 /* AuthenticateView.swift in Sources */, 738B1C3325C660140067407B /* ExpenseCD.swift in Sources */, 738B1C4325C6629D0067407B /* ColorExtension.swift in Sources */, diff --git a/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents b/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents index 11b08a8..34726c0 100644 --- a/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents +++ b/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents @@ -1,5 +1,5 @@ - + @@ -11,7 +11,17 @@ + + + + + + + + + + \ No newline at end of file diff --git a/Expenso/Library/CoreData/MonthlyExpenseCD.swift b/Expenso/Library/CoreData/MonthlyExpenseCD.swift new file mode 100644 index 0000000..f9620f3 --- /dev/null +++ b/Expenso/Library/CoreData/MonthlyExpenseCD.swift @@ -0,0 +1,30 @@ +// +// MonthlyExpenseCD.swift +// Expenso +// +// Created by Hendrik Steen on 31.05.22. +// + +import Foundation +import CoreData + +public class MonthlyExpenseCD: NSManagedObject, Identifiable { + @NSManaged public var type: String? + @NSManaged public var title: String? + @NSManaged public var tag: String? + @NSManaged public var note: String? + @NSManaged public var amount: Double + @NSManaged public var imageAttached: Data? + @NSManaged public var usingDate: Date? +} + +extension MonthlyExpenseCD { + static func getallDates(context: NSManagedObjectContext) { + let request: NSFetchRequest = MonthlyExpenseCD.fetchRequest() as! NSFetchRequest + request.sortDescriptors = [] + let allMonthlyExpenses = try! context.fetch(request) + for expense in allMonthlyExpenses { + print(expense.usingDate ?? Date()) + } + } +} diff --git a/Expenso/Screens/AddExpense/AddExpenseView.swift b/Expenso/Screens/AddExpense/AddExpenseView.swift index feb69fb..e6fa87b 100644 --- a/Expenso/Screens/AddExpense/AddExpenseView.swift +++ b/Expenso/Screens/AddExpense/AddExpenseView.swift @@ -158,6 +158,14 @@ struct AddExpenseView: View { VStack { Spacer() VStack { + Button(action: { viewModel.saveMonthlyTransaction(managedObjectContext: managedObjectContext) }, label: { + HStack { + Spacer() + TextView(text: "\(viewModel.getButtText()) every Month", type: .button).foregroundColor(.white) + Spacer() + } + }) + .padding(.vertical, 12).background(Color.main_color).cornerRadius(8) Button(action: { viewModel.saveTransaction(managedObjectContext: managedObjectContext) }, label: { HStack { Spacer() diff --git a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift index 085c918..3c7bf28 100644 --- a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift +++ b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift @@ -11,6 +11,7 @@ import CoreData class AddExpenseViewModel: ObservableObject { var expenseObj: ExpenseCD? + var monthlyExpenseObj: MonthlyExpenseCD? @Published var title = "" @Published var amount = "" @@ -139,4 +140,68 @@ class AddExpenseViewModel: ObservableObject { try managedObjectContext.save(); closePresenter = true } catch { alertMsg = "\(error)"; showAlert = true } } + + + func saveMonthlyTransaction(managedObjectContext: NSManagedObjectContext) { + let expense: MonthlyExpenseCD + let titleStr = title.trimmingCharacters(in: .whitespacesAndNewlines) + let amountStr = amount.trimmingCharacters(in: .whitespacesAndNewlines) + + if titleStr.isEmpty || titleStr == "" { + alertMsg = "Enter Title"; showAlert = true + return + } + if amountStr.isEmpty || amountStr == "" { + alertMsg = "Enter Amount"; showAlert = true + return + } + guard let amount = Double(amountStr) else { + alertMsg = "Enter valid number"; showAlert = true + return + } + guard amount >= 0 else { + alertMsg = "Amount can't be negative"; showAlert = true + return + } + guard amount <= 1000000000 else { + alertMsg = "Enter a smaller amount"; showAlert = true + return + } + + if monthlyExpenseObj != nil { + + expense = monthlyExpenseObj! + + if let image = imageAttached { + if imageUpdated { + if let _ = expense.imageAttached { + // Delete Previous Image from CoreData + } + expense.imageAttached = image.jpegData(compressionQuality: 1.0) + } + } else { + if let _ = expense.imageAttached { + // Delete Previous Image from CoreData + } + expense.imageAttached = nil + } + + } else { + expense = MonthlyExpenseCD(context: managedObjectContext) + expense.usingDate = Date() + if let image = imageAttached { + expense.imageAttached = image.jpegData(compressionQuality: 1.0) + } + } + expense.type = selectedType + expense.title = titleStr + expense.tag = selectedTag + expense.note = note + expense.amount = amount + do { + try managedObjectContext.save() + closePresenter = true + } catch { alertMsg = "\(error)"; showAlert = true } + } + } diff --git a/Expenso/Screens/Expense/ExpenseView.swift b/Expenso/Screens/Expense/ExpenseView.swift index d233cc6..b148727 100644 --- a/Expenso/Screens/Expense/ExpenseView.swift +++ b/Expenso/Screens/Expense/ExpenseView.swift @@ -58,6 +58,11 @@ struct ExpenseView: View { NavigationLink(destination: NavigationLazyView(AddExpenseView(viewModel: AddExpenseViewModel())), label: { Image("plus_icon").resizable().frame(width: 32.0, height: 32.0) }) .padding().background(Color.main_color).cornerRadius(35) + Button(action: { + MonthlyExpenseCD.getallDates(context: managedObjectContext) + }, label: { + Text("Test data") + }) } }.padding() } From 3e6b901e791c2569e3c97dca245b6afe0d24d052 Mon Sep 17 00:00:00 2001 From: comhendrik Date: Wed, 1 Jun 2022 21:20:02 +0200 Subject: [PATCH 2/9] option to create monthly expenses and add them automatically --- .DS_Store | Bin 6148 -> 6148 bytes .../Library/CoreData/MonthlyExpenseCD.swift | 7 +- .../AddExpense/AddExpenseViewModel.swift | 71 +++++++++++++----- Expenso/Screens/Expense/ExpenseView.swift | 3 +- 4 files changed, 58 insertions(+), 23 deletions(-) diff --git a/.DS_Store b/.DS_Store index 52169be979cbf4999c6cfb48cb6e5c547917e1e7..7b0e7f71ff1a69a209b82cbff28e37121c341f13 100644 GIT binary patch delta 80 zcmZoMXffCz&c@8V^VZ}CtXd+u`7SO=Ir&Kp3=ABp(t9sOD<5%$N~B NSFetchRequest { let request: NSFetchRequest = MonthlyExpenseCD.fetchRequest() as! NSFetchRequest request.sortDescriptors = [] - let allMonthlyExpenses = try! context.fetch(request) - for expense in allMonthlyExpenses { - print(expense.usingDate ?? Date()) - } + return request } } diff --git a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift index 3c7bf28..0050bd0 100644 --- a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift +++ b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift @@ -172,26 +172,26 @@ class AddExpenseViewModel: ObservableObject { expense = monthlyExpenseObj! - if let image = imageAttached { - if imageUpdated { - if let _ = expense.imageAttached { - // Delete Previous Image from CoreData - } - expense.imageAttached = image.jpegData(compressionQuality: 1.0) - } - } else { - if let _ = expense.imageAttached { - // Delete Previous Image from CoreData - } - expense.imageAttached = nil - } +// if let image = imageAttached { +// if imageUpdated { +// if let _ = expense.imageAttached { +// // Delete Previous Image from CoreData +// } +// expense.imageAttached = image.jpegData(compressionQuality: 1.0) +// } +// } else { +// if let _ = expense.imageAttached { +// // Delete Previous Image from CoreData +// } +// expense.imageAttached = nil +// } } else { expense = MonthlyExpenseCD(context: managedObjectContext) - expense.usingDate = Date() - if let image = imageAttached { - expense.imageAttached = image.jpegData(compressionQuality: 1.0) - } + expense.usingDate = occuredOn +// if let image = imageAttached { +// expense.imageAttached = image.jpegData(compressionQuality: 1.0) +// } } expense.type = selectedType expense.title = titleStr @@ -204,4 +204,41 @@ class AddExpenseViewModel: ObservableObject { } catch { alertMsg = "\(error)"; showAlert = true } } + func checkAllMonthlyExpenses(managedObjectContext: NSManagedObjectContext, request: NSFetchRequest) { + do { + let allMonthlyExpenses = try? managedObjectContext.fetch(request) + if allMonthlyExpenses == nil { + print("error") + return + } + for expense in allMonthlyExpenses! { + print(expense.usingDate ?? Date()) + if expense.usingDate == nil { + continue + } + if Calendar.current.isDateInToday(expense.usingDate!) { + if let date = Calendar.current.date(byAdding: .month, value: 1, to: expense.usingDate!) { + expense.usingDate = date + print(expense.usingDate!) + let newExpense = ExpenseCD(context: managedObjectContext) + newExpense.amount = expense.amount + newExpense.createdAt = Date() + //newExpense.imageAttached = expense.imageAttached + newExpense.note = expense.note + newExpense.occuredOn = Date() + newExpense.tag = expense.tag + newExpense.title = expense.title + newExpense.type = expense.type + newExpense.updatedAt = Date() + try managedObjectContext.save() + } + } else { + print("not current date") + } + } + } catch { + print(error) + } + } + } diff --git a/Expenso/Screens/Expense/ExpenseView.swift b/Expenso/Screens/Expense/ExpenseView.swift index b148727..4285318 100644 --- a/Expenso/Screens/Expense/ExpenseView.swift +++ b/Expenso/Screens/Expense/ExpenseView.swift @@ -21,6 +21,7 @@ struct ExpenseView: View { @State private var displayAbout = false @State private var displaySettings = false + var body: some View { NavigationView { ZStack { @@ -59,7 +60,7 @@ struct ExpenseView: View { label: { Image("plus_icon").resizable().frame(width: 32.0, height: 32.0) }) .padding().background(Color.main_color).cornerRadius(35) Button(action: { - MonthlyExpenseCD.getallDates(context: managedObjectContext) + AddExpenseViewModel().checkAllMonthlyExpenses(managedObjectContext: managedObjectContext, request: MonthlyExpenseCD.getallDates(context: managedObjectContext)) }, label: { Text("Test data") }) From b5174d177dfe3388467e0ef959cc21e1e36d9b3d Mon Sep 17 00:00:00 2001 From: comhendrik Date: Wed, 1 Jun 2022 21:47:24 +0200 Subject: [PATCH 3/9] Init MonthlyExpensesSettingsView --- .DS_Store | Bin 6148 -> 6148 bytes Expenso.xcodeproj/project.pbxproj | 12 ++++++++ .../Library/CoreData/MonthlyExpenseCD.swift | 2 +- Expenso/Screens/Expense/ExpenseView.swift | 5 +++- .../MonthlyExpenseSettingsView.swift | 28 ++++++++++++++++++ 5 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 Expenso/Screens/MonthlyExpensesSettings/MonthlyExpenseSettingsView.swift diff --git a/.DS_Store b/.DS_Store index 7b0e7f71ff1a69a209b82cbff28e37121c341f13..3e452b5441e6447e15f4ee45c33eabd39e6d414e 100644 GIT binary patch delta 29 lcmZoMXffCz!N&Av(&PuMT9aMax)=>M7FsZFX6N|J4*;FE3K{?a delta 27 jcmZoMXffCz!N#=n*5n7QT9aMax;7SCGHzz)_{$FfkC_So diff --git a/Expenso.xcodeproj/project.pbxproj b/Expenso.xcodeproj/project.pbxproj index c238e47..7534f7e 100644 --- a/Expenso.xcodeproj/project.pbxproj +++ b/Expenso.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 24AE70432847F81100B2BD49 /* MonthlyExpenseSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24AE70422847F81100B2BD49 /* MonthlyExpenseSettingsView.swift */; }; 24DF38B62846B7BC006CA05F /* MonthlyExpenseCD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24DF38B52846B7BC006CA05F /* MonthlyExpenseCD.swift */; }; 736C720A25CFD89900720DEA /* empty-face.json in Resources */ = {isa = PBXBuildFile; fileRef = 736C720925CFD89900720DEA /* empty-face.json */; }; 736C721B25CFE8E200720DEA /* LottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736C721A25CFE8E200720DEA /* LottieView.swift */; }; @@ -54,6 +55,7 @@ /* Begin PBXFileReference section */ 1B0E373FB89DD0864398FEE7 /* Pods_Expenso.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Expenso.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 24AE70422847F81100B2BD49 /* MonthlyExpenseSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyExpenseSettingsView.swift; sourceTree = ""; }; 24DF38B52846B7BC006CA05F /* MonthlyExpenseCD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyExpenseCD.swift; sourceTree = ""; }; 2C2D1600DAC62FA04EDCF170 /* Pods-Expenso.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Expenso.debug.xcconfig"; path = "Target Support Files/Pods-Expenso/Pods-Expenso.debug.xcconfig"; sourceTree = ""; }; 736C720925CFD89900720DEA /* empty-face.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "empty-face.json"; sourceTree = ""; }; @@ -114,9 +116,18 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 24AE70412847F7F900B2BD49 /* MonthlyExpensesSettings */ = { + isa = PBXGroup; + children = ( + 24AE70422847F81100B2BD49 /* MonthlyExpenseSettingsView.swift */, + ); + path = MonthlyExpensesSettings; + sourceTree = ""; + }; 733763A827120DB600AA983A /* Screens */ = { isa = PBXGroup; children = ( + 24AE70412847F7F900B2BD49 /* MonthlyExpensesSettings */, 739DFF7925DC1E23005BD5C8 /* Authenticate */, 738B1C8E25C680C50067407B /* About */, 738B1C8925C67D790067407B /* ExpenseFilter */, @@ -455,6 +466,7 @@ 73998C5525DA5578007D735B /* AttachmentHandler.swift in Sources */, 738B1C8425C6764B0067407B /* ExpenseDetailedViewModel.swift in Sources */, 738B1C9025C680D20067407B /* AboutView.swift in Sources */, + 24AE70432847F81100B2BD49 /* MonthlyExpenseSettingsView.swift in Sources */, 738B1C4F25C663180067407B /* DateExtension.swift in Sources */, 738B1C5225C6632F0067407B /* HelperMethods.swift in Sources */, 738B1C2425C65E060067407B /* Expenso.xcdatamodeld in Sources */, diff --git a/Expenso/Library/CoreData/MonthlyExpenseCD.swift b/Expenso/Library/CoreData/MonthlyExpenseCD.swift index faf5d78..ab00e94 100644 --- a/Expenso/Library/CoreData/MonthlyExpenseCD.swift +++ b/Expenso/Library/CoreData/MonthlyExpenseCD.swift @@ -19,7 +19,7 @@ public class MonthlyExpenseCD: NSManagedObject, Identifiable { } extension MonthlyExpenseCD { - static func getallDates(context: NSManagedObjectContext) -> NSFetchRequest { + static func getAllMonthlyExpenseData() -> NSFetchRequest { let request: NSFetchRequest = MonthlyExpenseCD.fetchRequest() as! NSFetchRequest request.sortDescriptors = [] return request diff --git a/Expenso/Screens/Expense/ExpenseView.swift b/Expenso/Screens/Expense/ExpenseView.swift index 4285318..796c6ab 100644 --- a/Expenso/Screens/Expense/ExpenseView.swift +++ b/Expenso/Screens/Expense/ExpenseView.swift @@ -20,6 +20,7 @@ struct ExpenseView: View { @State private var showOptionsSheet = false @State private var displayAbout = false @State private var displaySettings = false + @State private var displayMonthlyExpenses = false var body: some View { @@ -30,6 +31,7 @@ struct ExpenseView: View { VStack { NavigationLink(destination: NavigationLazyView(ExpenseSettingsView()), isActive: $displaySettings, label: {}) NavigationLink(destination: NavigationLazyView(AboutView()), isActive: $displayAbout, label: {}) + NavigationLink(destination: NavigationLazyView(MonthlyExpenseSettingsView()), isActive: $displayMonthlyExpenses, label: {}) ToolbarModelView(title: "Dashboard", hasBackButt: false, button1Icon: IMAGE_OPTION_ICON, button2Icon: IMAGE_FILTER_ICON) { self.presentationMode.wrappedValue.dismiss() } button1Method: { self.showOptionsSheet = true } button2Method: { self.showFilterSheet = true } @@ -46,6 +48,7 @@ struct ExpenseView: View { ActionSheet(title: Text("Select an option"), buttons: [ .default(Text("About")) { self.displayAbout = true }, .default(Text("Settings")) { self.displaySettings = true }, + .default(Text("Monthly Expenses")) { self.displayMonthlyExpenses = true }, .cancel() ]) } @@ -60,7 +63,7 @@ struct ExpenseView: View { label: { Image("plus_icon").resizable().frame(width: 32.0, height: 32.0) }) .padding().background(Color.main_color).cornerRadius(35) Button(action: { - AddExpenseViewModel().checkAllMonthlyExpenses(managedObjectContext: managedObjectContext, request: MonthlyExpenseCD.getallDates(context: managedObjectContext)) + AddExpenseViewModel().checkAllMonthlyExpenses(managedObjectContext: managedObjectContext, request: MonthlyExpenseCD.getAllMonthlyExpenseData()) }, label: { Text("Test data") }) diff --git a/Expenso/Screens/MonthlyExpensesSettings/MonthlyExpenseSettingsView.swift b/Expenso/Screens/MonthlyExpensesSettings/MonthlyExpenseSettingsView.swift new file mode 100644 index 0000000..c3031e9 --- /dev/null +++ b/Expenso/Screens/MonthlyExpensesSettings/MonthlyExpenseSettingsView.swift @@ -0,0 +1,28 @@ +// +// MonthlyExpenseSettingsView.swift +// Expenso +// +// Created by Hendrik Steen on 01.06.22. +// + +import SwiftUI + +struct MonthlyExpenseSettingsView: View { + @FetchRequest(fetchRequest: MonthlyExpenseCD.getAllMonthlyExpenseData()) var monthlyExpenses: FetchedResults + var body: some View { + ScrollView { + ForEach(monthlyExpenses) { monthlyExpenseObj in + HStack { + Text(monthlyExpenseObj.title ?? "no title") + Text(monthlyExpenseObj.tag ?? "no tag") + } + } + } + } +} + +struct MonthlyExpenseSettingsView_Previews: PreviewProvider { + static var previews: some View { + MonthlyExpenseSettingsView() + } +} From c120265757f5f5e0f42569895137ccbe72ca43d5 Mon Sep 17 00:00:00 2001 From: comhendrik Date: Thu, 2 Jun 2022 17:14:18 +0200 Subject: [PATCH 4/9] ui for monthly transactions view --- .DS_Store | Bin 6148 -> 6148 bytes Expenso.xcodeproj/project.pbxproj | 30 ++++-- .../Screens/AddExpense/AddExpenseView.swift | 18 ++-- .../AddExpense/AddExpenseViewModel.swift | 14 ++- Expenso/Screens/Expense/ExpenseView.swift | 6 +- .../MonthlyExpenseSettingsView.swift | 28 ------ .../MonthlyTransactionDetailedView.swift | 92 ++++++++++++++++++ .../MonthlyTransactionDetailedViewModel.swift | 43 ++++++++ .../MonthlyTransactionSettingsView.swift | 74 ++++++++++++++ 9 files changed, 256 insertions(+), 49 deletions(-) delete mode 100644 Expenso/Screens/MonthlyExpensesSettings/MonthlyExpenseSettingsView.swift create mode 100644 Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift create mode 100644 Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift create mode 100644 Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift diff --git a/.DS_Store b/.DS_Store index 3e452b5441e6447e15f4ee45c33eabd39e6d414e..25956a82341eb9035faafd7cba5133c477f219da 100644 GIT binary patch delta 18 ZcmZoMXffCz$;P;F@&i__%`R*Q1OYsl21)<` delta 18 ZcmZoMXffCz$;LQo@&i__%`R*Q1OYq(20H)% diff --git a/Expenso.xcodeproj/project.pbxproj b/Expenso.xcodeproj/project.pbxproj index 7534f7e..51a60c9 100644 --- a/Expenso.xcodeproj/project.pbxproj +++ b/Expenso.xcodeproj/project.pbxproj @@ -7,7 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - 24AE70432847F81100B2BD49 /* MonthlyExpenseSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24AE70422847F81100B2BD49 /* MonthlyExpenseSettingsView.swift */; }; + 243DEC7A2848FCC1001281AD /* MonthlyTransactionDetailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243DEC792848FCC1001281AD /* MonthlyTransactionDetailedView.swift */; }; + 243DEC7C2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243DEC7B2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift */; }; + 24AE70432847F81100B2BD49 /* MonthlyTransactionSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24AE70422847F81100B2BD49 /* MonthlyTransactionSettingsView.swift */; }; 24DF38B62846B7BC006CA05F /* MonthlyExpenseCD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24DF38B52846B7BC006CA05F /* MonthlyExpenseCD.swift */; }; 736C720A25CFD89900720DEA /* empty-face.json in Resources */ = {isa = PBXBuildFile; fileRef = 736C720925CFD89900720DEA /* empty-face.json */; }; 736C721B25CFE8E200720DEA /* LottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736C721A25CFE8E200720DEA /* LottieView.swift */; }; @@ -55,7 +57,9 @@ /* Begin PBXFileReference section */ 1B0E373FB89DD0864398FEE7 /* Pods_Expenso.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Expenso.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 24AE70422847F81100B2BD49 /* MonthlyExpenseSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyExpenseSettingsView.swift; sourceTree = ""; }; + 243DEC792848FCC1001281AD /* MonthlyTransactionDetailedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionDetailedView.swift; sourceTree = ""; }; + 243DEC7B2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionDetailedViewModel.swift; sourceTree = ""; }; + 24AE70422847F81100B2BD49 /* MonthlyTransactionSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionSettingsView.swift; sourceTree = ""; }; 24DF38B52846B7BC006CA05F /* MonthlyExpenseCD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyExpenseCD.swift; sourceTree = ""; }; 2C2D1600DAC62FA04EDCF170 /* Pods-Expenso.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Expenso.debug.xcconfig"; path = "Target Support Files/Pods-Expenso/Pods-Expenso.debug.xcconfig"; sourceTree = ""; }; 736C720925CFD89900720DEA /* empty-face.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "empty-face.json"; sourceTree = ""; }; @@ -116,18 +120,28 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 24AE70412847F7F900B2BD49 /* MonthlyExpensesSettings */ = { + 243DEC762848FB45001281AD /* MonthlyTransactionSettings */ = { isa = PBXGroup; children = ( - 24AE70422847F81100B2BD49 /* MonthlyExpenseSettingsView.swift */, + 24AE70422847F81100B2BD49 /* MonthlyTransactionSettingsView.swift */, ); - path = MonthlyExpensesSettings; + path = MonthlyTransactionSettings; + sourceTree = ""; + }; + 243DEC782848FCAB001281AD /* MonthlyTransactionDetailed */ = { + isa = PBXGroup; + children = ( + 243DEC792848FCC1001281AD /* MonthlyTransactionDetailedView.swift */, + 243DEC7B2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift */, + ); + path = MonthlyTransactionDetailed; sourceTree = ""; }; 733763A827120DB600AA983A /* Screens */ = { isa = PBXGroup; children = ( - 24AE70412847F7F900B2BD49 /* MonthlyExpensesSettings */, + 243DEC782848FCAB001281AD /* MonthlyTransactionDetailed */, + 243DEC762848FB45001281AD /* MonthlyTransactionSettings */, 739DFF7925DC1E23005BD5C8 /* Authenticate */, 738B1C8E25C680C50067407B /* About */, 738B1C8925C67D790067407B /* ExpenseFilter */, @@ -464,9 +478,10 @@ files = ( 738B1C3825C661750067407B /* ExpenseView.swift in Sources */, 73998C5525DA5578007D735B /* AttachmentHandler.swift in Sources */, + 243DEC7A2848FCC1001281AD /* MonthlyTransactionDetailedView.swift in Sources */, 738B1C8425C6764B0067407B /* ExpenseDetailedViewModel.swift in Sources */, 738B1C9025C680D20067407B /* AboutView.swift in Sources */, - 24AE70432847F81100B2BD49 /* MonthlyExpenseSettingsView.swift in Sources */, + 24AE70432847F81100B2BD49 /* MonthlyTransactionSettingsView.swift in Sources */, 738B1C4F25C663180067407B /* DateExtension.swift in Sources */, 738B1C5225C6632F0067407B /* HelperMethods.swift in Sources */, 738B1C2425C65E060067407B /* Expenso.xcdatamodeld in Sources */, @@ -490,6 +505,7 @@ 738B1C2F25C65EF70067407B /* Configs.swift in Sources */, 73E688DA25FF32790000462A /* ChartView.swift in Sources */, 736C721B25CFE8E200720DEA /* LottieView.swift in Sources */, + 243DEC7C2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift in Sources */, 738B1C7725C675200067407B /* AddExpenseView.swift in Sources */, 738B1C8025C6763E0067407B /* ExpenseDetailedView.swift in Sources */, ); diff --git a/Expenso/Screens/AddExpense/AddExpenseView.swift b/Expenso/Screens/AddExpense/AddExpenseView.swift index e6fa87b..7a96df2 100644 --- a/Expenso/Screens/AddExpense/AddExpenseView.swift +++ b/Expenso/Screens/AddExpense/AddExpenseView.swift @@ -158,14 +158,16 @@ struct AddExpenseView: View { VStack { Spacer() VStack { - Button(action: { viewModel.saveMonthlyTransaction(managedObjectContext: managedObjectContext) }, label: { - HStack { - Spacer() - TextView(text: "\(viewModel.getButtText()) every Month", type: .button).foregroundColor(.white) - Spacer() - } - }) - .padding(.vertical, 12).background(Color.main_color).cornerRadius(8) + if viewModel.expenseObj == nil { + Button(action: { viewModel.saveMonthlyTransaction(managedObjectContext: managedObjectContext) }, label: { + HStack { + Spacer() + TextView(text: "\(viewModel.getButtText()) every Month", type: .button).foregroundColor(.white) + Spacer() + } + }) + .padding(.vertical, 12).background(Color.main_color).cornerRadius(8) + } Button(action: { viewModel.saveTransaction(managedObjectContext: managedObjectContext) }, label: { HStack { Spacer() diff --git a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift index 0050bd0..a1a67f2 100644 --- a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift +++ b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift @@ -11,7 +11,7 @@ import CoreData class AddExpenseViewModel: ObservableObject { var expenseObj: ExpenseCD? - var monthlyExpenseObj: MonthlyExpenseCD? + var monthlyTransactionObj: MonthlyExpenseCD? @Published var title = "" @Published var amount = "" @@ -168,9 +168,9 @@ class AddExpenseViewModel: ObservableObject { return } - if monthlyExpenseObj != nil { + if monthlyTransactionObj != nil { - expense = monthlyExpenseObj! + expense = monthlyTransactionObj! // if let image = imageAttached { // if imageUpdated { @@ -204,6 +204,14 @@ class AddExpenseViewModel: ObservableObject { } catch { alertMsg = "\(error)"; showAlert = true } } + func deleteMonthlyTransaction(managedObjectContext: NSManagedObjectContext) { + guard let monthlyTransactionObj = monthlyTransactionObj else { return } + managedObjectContext.delete(monthlyTransactionObj) + do { + try managedObjectContext.save(); closePresenter = true + } catch { alertMsg = "\(error)"; showAlert = true } + } + func checkAllMonthlyExpenses(managedObjectContext: NSManagedObjectContext, request: NSFetchRequest) { do { let allMonthlyExpenses = try? managedObjectContext.fetch(request) diff --git a/Expenso/Screens/Expense/ExpenseView.swift b/Expenso/Screens/Expense/ExpenseView.swift index 796c6ab..e92a739 100644 --- a/Expenso/Screens/Expense/ExpenseView.swift +++ b/Expenso/Screens/Expense/ExpenseView.swift @@ -20,7 +20,7 @@ struct ExpenseView: View { @State private var showOptionsSheet = false @State private var displayAbout = false @State private var displaySettings = false - @State private var displayMonthlyExpenses = false + @State private var displayMonthlyTransaction = false var body: some View { @@ -31,7 +31,7 @@ struct ExpenseView: View { VStack { NavigationLink(destination: NavigationLazyView(ExpenseSettingsView()), isActive: $displaySettings, label: {}) NavigationLink(destination: NavigationLazyView(AboutView()), isActive: $displayAbout, label: {}) - NavigationLink(destination: NavigationLazyView(MonthlyExpenseSettingsView()), isActive: $displayMonthlyExpenses, label: {}) + NavigationLink(destination: NavigationLazyView(MonthlyTransactionSettingsView()), isActive: $displayMonthlyTransaction, label: {}) ToolbarModelView(title: "Dashboard", hasBackButt: false, button1Icon: IMAGE_OPTION_ICON, button2Icon: IMAGE_FILTER_ICON) { self.presentationMode.wrappedValue.dismiss() } button1Method: { self.showOptionsSheet = true } button2Method: { self.showFilterSheet = true } @@ -48,7 +48,7 @@ struct ExpenseView: View { ActionSheet(title: Text("Select an option"), buttons: [ .default(Text("About")) { self.displayAbout = true }, .default(Text("Settings")) { self.displaySettings = true }, - .default(Text("Monthly Expenses")) { self.displayMonthlyExpenses = true }, + .default(Text("Monthly Transactions")) { self.displayMonthlyTransaction = true }, .cancel() ]) } diff --git a/Expenso/Screens/MonthlyExpensesSettings/MonthlyExpenseSettingsView.swift b/Expenso/Screens/MonthlyExpensesSettings/MonthlyExpenseSettingsView.swift deleted file mode 100644 index c3031e9..0000000 --- a/Expenso/Screens/MonthlyExpensesSettings/MonthlyExpenseSettingsView.swift +++ /dev/null @@ -1,28 +0,0 @@ -// -// MonthlyExpenseSettingsView.swift -// Expenso -// -// Created by Hendrik Steen on 01.06.22. -// - -import SwiftUI - -struct MonthlyExpenseSettingsView: View { - @FetchRequest(fetchRequest: MonthlyExpenseCD.getAllMonthlyExpenseData()) var monthlyExpenses: FetchedResults - var body: some View { - ScrollView { - ForEach(monthlyExpenses) { monthlyExpenseObj in - HStack { - Text(monthlyExpenseObj.title ?? "no title") - Text(monthlyExpenseObj.tag ?? "no tag") - } - } - } - } -} - -struct MonthlyExpenseSettingsView_Previews: PreviewProvider { - static var previews: some View { - MonthlyExpenseSettingsView() - } -} diff --git a/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift b/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift new file mode 100644 index 0000000..824566d --- /dev/null +++ b/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift @@ -0,0 +1,92 @@ +// +// MonthlyTransactionDetailedView.swift +// Expenso +// +// Created by Hendrik Steen on 02.06.22. +// + +import SwiftUI + +struct MonthlyTransactionDetailedView: View { + + @Environment(\.presentationMode) var presentationMode: Binding + // CoreData + @Environment(\.managedObjectContext) var managedObjectContext + + @ObservedObject private var viewModel: MonthlyTransactionDetailedViewModel + @AppStorage(UD_EXPENSE_CURRENCY) var CURRENCY: String = "" + + @State private var confirmDelete = false + + init(monthlyTransactionObj: MonthlyExpenseCD) { + viewModel = MonthlyTransactionDetailedViewModel(monthlyTransactionObj: monthlyTransactionObj) + } + + var body: some View { + NavigationView { + ZStack { + Color.primary_color.edgesIgnoringSafeArea(.all) + + VStack { + + ToolbarModelView(title: "Details", button1Icon: IMAGE_DELETE_ICON, button2Icon: IMAGE_SHARE_ICON) { self.presentationMode.wrappedValue.dismiss() } + button1Method: { self.confirmDelete = true } + button2Method: { viewModel.shareNote() } + + ScrollView(showsIndicators: false) { + + VStack(spacing: 24) { + ExpenseDetailedListView(title: "Title", description: viewModel.monthlyTransactionObj.title ?? "") + ExpenseDetailedListView(title: "Amount", description: "\(CURRENCY)\(viewModel.monthlyTransactionObj.amount)") + ExpenseDetailedListView(title: "Transaction type", description: viewModel.monthlyTransactionObj.type == TRANS_TYPE_INCOME ? "Income" : "Expense" ) + ExpenseDetailedListView(title: "Tag", description: getTransTagTitle(transTag: viewModel.monthlyTransactionObj.tag ?? "")) + ExpenseDetailedListView(title: "When", description: getDateFormatter(date: viewModel.monthlyTransactionObj.usingDate, format: "EEEE, dd MMM hh:mm a")) + if let note = viewModel.monthlyTransactionObj.note, note != "" { + ExpenseDetailedListView(title: "Note", description: note) + } + if let data = viewModel.monthlyTransactionObj.imageAttached { + VStack(spacing: 8) { + HStack { TextView(text: "Attachment", type: .caption).foregroundColor(Color.init(hex: "828282")); Spacer() } + Image(uiImage: UIImage(data: data)!) + .resizable() + .scaledToFill() + .frame(height: 250).frame(maxWidth: .infinity) + .background(Color.secondary_color) + .cornerRadius(4) + } + } + }.padding(16) + + Spacer().frame(height: 24) + Spacer() + } + .alert(isPresented: $confirmDelete, + content: { + Alert(title: Text(APP_NAME), message: Text("Are you sure you want to delete this transaction?"), + primaryButton: .destructive(Text("Delete")) { + viewModel.deleteNote(managedObjectContext: managedObjectContext) + }, secondaryButton: Alert.Button.cancel(Text("Cancel"), action: { confirmDelete = false }) + ) + }) + }.edgesIgnoringSafeArea(.all) + //TODO: Edit View for Monthly Expense +// VStack { +// Spacer() +// HStack { +// Spacer() +// NavigationLink(destination: EditMonthlyTransactionView(viewModel: AddExpenseViewModel(expenseObj: nil, monthlyTransactionObj: viewModel.monthlyTransactionObj)), label: { +// Image("pencil_icon").resizable().frame(width: 28.0, height: 28.0) +// Text("Edit").modifier(InterFont(.semiBold, size: 18)).foregroundColor(.white) +// }) +// .padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 20)) +// .background(Color.main_color).cornerRadius(25) +// }.padding(24) +// } + } + .navigationBarHidden(true) + } + .navigationViewStyle(StackNavigationViewStyle()) + .navigationBarHidden(true) + .navigationBarBackButtonHidden(true) + } +} diff --git a/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift b/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift new file mode 100644 index 0000000..f4f691d --- /dev/null +++ b/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift @@ -0,0 +1,43 @@ +// +// MonthlyTransactionDetailedViewModel.swift +// Expenso +// +// Created by Hendrik Steen on 02.06.22. +// + +import UIKit +import CoreData + +class MonthlyTransactionDetailedViewModel: ObservableObject { + + @Published var monthlyTransactionObj: MonthlyExpenseCD + + @Published var alertMsg = String() + @Published var showAlert = false + @Published var closePresenter = false + + init(monthlyTransactionObj: MonthlyExpenseCD) { + self.monthlyTransactionObj = monthlyTransactionObj + } + + func deleteNote(managedObjectContext: NSManagedObjectContext) { + managedObjectContext.delete(monthlyTransactionObj) + do { + try managedObjectContext.save(); closePresenter = true + } catch { alertMsg = "\(error)"; showAlert = true } + } + + func shareNote() { + let shareStr = """ + Title: \(monthlyTransactionObj.title ?? "") + Amount: \(UserDefaults.standard.string(forKey: UD_EXPENSE_CURRENCY) ?? "")\(monthlyTransactionObj.amount) + monthlyTransactionObj Category: \(getTransTagTitle(transTag: monthlyTransactionObj.tag ?? "")) + Date to charge: \(getDateFormatter(date: monthlyTransactionObj.usingDate, format: "EEEE, dd MMM hh:mm a")) + Note: \(monthlyTransactionObj.note ?? "") + + \(SHARED_FROM_EXPENSO) + """ + let av = UIActivityViewController(activityItems: [shareStr], applicationActivities: nil) + UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil) + } +} diff --git a/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift b/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift new file mode 100644 index 0000000..9edb889 --- /dev/null +++ b/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift @@ -0,0 +1,74 @@ +// +// MonthlyTransactionSettingsView.swift +// Expenso +// +// Created by Hendrik Steen on 01.06.22. +// + +import SwiftUI + +struct MonthlyTransactionSettingsView: View { + @Environment(\.presentationMode) var presentationMode: Binding + + @FetchRequest(fetchRequest: MonthlyExpenseCD.getAllMonthlyExpenseData()) var monthlyTransactions: FetchedResults + var body: some View { + NavigationView { + ZStack { + Color.primary_color.edgesIgnoringSafeArea(.all) + + VStack { + ToolbarModelView(title: "Monthly Transaction") { self.presentationMode.wrappedValue.dismiss() } + ScrollView { + ForEach(self.monthlyTransactions) { monthlyTransactionObj in + NavigationLink(destination: MonthlyTransactionDetailedView(monthlyTransactionObj: monthlyTransactionObj), label: { MonthlyTransView(monthlyTransactionObj: monthlyTransactionObj) }) + } + + } + .padding(.horizontal, 8) + }.edgesIgnoringSafeArea(.all) + } + .navigationBarHidden(true) + + } + .navigationViewStyle(StackNavigationViewStyle()) + .navigationBarHidden(true) + .navigationBarBackButtonHidden(true) + + } + +} + +struct MonthlyTransView: View { + + @ObservedObject var monthlyTransactionObj: MonthlyExpenseCD + @AppStorage(UD_EXPENSE_CURRENCY) var CURRENCY: String = "" + + var body: some View { + HStack { + + NavigationLink(destination: NavigationLazyView(ExpenseFilterView(categTag: monthlyTransactionObj.tag)), label: { + Image(getTransTagIcon(transTag: monthlyTransactionObj.tag ?? "")) + .resizable().frame(width: 24, height: 24).padding(16) + .background(Color.primary_color).cornerRadius(4) + }) + + VStack(alignment: .leading, spacing: 6) { + HStack { + TextView(text: monthlyTransactionObj.title ?? "", type: .subtitle_1, lineLimit: 1).foregroundColor(Color.text_primary_color) + Spacer() + TextView(text: "\(monthlyTransactionObj.type == TRANS_TYPE_INCOME ? "+" : "-")\(CURRENCY)\(monthlyTransactionObj.amount)", type: .subtitle_1) + .foregroundColor(monthlyTransactionObj.type == TRANS_TYPE_INCOME ? Color.main_green : Color.main_red) + } + HStack { + TextView(text: getTransTagTitle(transTag: monthlyTransactionObj.tag ?? ""), type: .body_2).foregroundColor(Color.text_primary_color) + Spacer() + TextView(text: "made on: ", type: .body_2).foregroundColor(Color.text_primary_color) + TextView(text: getDateFormatter(date: monthlyTransactionObj.usingDate, format: "MMM dd, yyyy"), type: .body_2).foregroundColor(Color.text_primary_color) + } + }.padding(.leading, 4) + + Spacer() + + }.padding(8).background(Color.secondary_color).cornerRadius(4) + } +} From 6145bebca5e519219130d9b77be160db4a1d7adf Mon Sep 17 00:00:00 2001 From: comhendrik Date: Fri, 3 Jun 2022 14:58:43 +0200 Subject: [PATCH 5/9] Monthly Transaction 1.0 --- Expenso.xcodeproj/project.pbxproj | 12 +- .../Expenso.xcdatamodel/contents | 4 +- ...nseCD.swift => MonthlyTransactionCD.swift} | 8 +- .../AddExpense/AddExpenseViewModel.swift | 93 ++++++--- Expenso/Screens/Expense/ExpenseView.swift | 2 +- .../EditMonthlyTransactionView.swift | 186 ++++++++++++++++++ .../MonthlyTransactionDetailedView.swift | 29 ++- .../MonthlyTransactionDetailedViewModel.swift | 6 +- .../MonthlyTransactionSettingsView.swift | 6 +- 9 files changed, 282 insertions(+), 64 deletions(-) rename Expenso/Library/CoreData/{MonthlyExpenseCD.swift => MonthlyTransactionCD.swift} (68%) create mode 100644 Expenso/Screens/MonthlyTransactionDetailed/EditMonthlyTransactionView.swift diff --git a/Expenso.xcodeproj/project.pbxproj b/Expenso.xcodeproj/project.pbxproj index 51a60c9..23ea29d 100644 --- a/Expenso.xcodeproj/project.pbxproj +++ b/Expenso.xcodeproj/project.pbxproj @@ -9,8 +9,9 @@ /* Begin PBXBuildFile section */ 243DEC7A2848FCC1001281AD /* MonthlyTransactionDetailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243DEC792848FCC1001281AD /* MonthlyTransactionDetailedView.swift */; }; 243DEC7C2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243DEC7B2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift */; }; + 2463680C284A35CE007FC25F /* EditMonthlyTransactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2463680B284A35CE007FC25F /* EditMonthlyTransactionView.swift */; }; 24AE70432847F81100B2BD49 /* MonthlyTransactionSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24AE70422847F81100B2BD49 /* MonthlyTransactionSettingsView.swift */; }; - 24DF38B62846B7BC006CA05F /* MonthlyExpenseCD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24DF38B52846B7BC006CA05F /* MonthlyExpenseCD.swift */; }; + 24DF38B62846B7BC006CA05F /* MonthlyTransactionCD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24DF38B52846B7BC006CA05F /* MonthlyTransactionCD.swift */; }; 736C720A25CFD89900720DEA /* empty-face.json in Resources */ = {isa = PBXBuildFile; fileRef = 736C720925CFD89900720DEA /* empty-face.json */; }; 736C721B25CFE8E200720DEA /* LottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736C721A25CFE8E200720DEA /* LottieView.swift */; }; 738B1C1825C65DFE0067407B /* ExpensoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738B1C1725C65DFE0067407B /* ExpensoApp.swift */; }; @@ -59,8 +60,9 @@ 1B0E373FB89DD0864398FEE7 /* Pods_Expenso.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Expenso.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 243DEC792848FCC1001281AD /* MonthlyTransactionDetailedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionDetailedView.swift; sourceTree = ""; }; 243DEC7B2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionDetailedViewModel.swift; sourceTree = ""; }; + 2463680B284A35CE007FC25F /* EditMonthlyTransactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMonthlyTransactionView.swift; sourceTree = ""; }; 24AE70422847F81100B2BD49 /* MonthlyTransactionSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionSettingsView.swift; sourceTree = ""; }; - 24DF38B52846B7BC006CA05F /* MonthlyExpenseCD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyExpenseCD.swift; sourceTree = ""; }; + 24DF38B52846B7BC006CA05F /* MonthlyTransactionCD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionCD.swift; sourceTree = ""; }; 2C2D1600DAC62FA04EDCF170 /* Pods-Expenso.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Expenso.debug.xcconfig"; path = "Target Support Files/Pods-Expenso/Pods-Expenso.debug.xcconfig"; sourceTree = ""; }; 736C720925CFD89900720DEA /* empty-face.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "empty-face.json"; sourceTree = ""; }; 736C721A25CFE8E200720DEA /* LottieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieView.swift; sourceTree = ""; }; @@ -132,6 +134,7 @@ isa = PBXGroup; children = ( 243DEC792848FCC1001281AD /* MonthlyTransactionDetailedView.swift */, + 2463680B284A35CE007FC25F /* EditMonthlyTransactionView.swift */, 243DEC7B2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift */, ); path = MonthlyTransactionDetailed; @@ -226,7 +229,7 @@ isa = PBXGroup; children = ( 738B1C3225C660140067407B /* ExpenseCD.swift */, - 24DF38B52846B7BC006CA05F /* MonthlyExpenseCD.swift */, + 24DF38B52846B7BC006CA05F /* MonthlyTransactionCD.swift */, ); path = CoreData; sourceTree = ""; @@ -486,7 +489,7 @@ 738B1C5225C6632F0067407B /* HelperMethods.swift in Sources */, 738B1C2425C65E060067407B /* Expenso.xcdatamodeld in Sources */, 738B1C8B25C67D880067407B /* ExpenseFilterView.swift in Sources */, - 24DF38B62846B7BC006CA05F /* MonthlyExpenseCD.swift in Sources */, + 24DF38B62846B7BC006CA05F /* MonthlyTransactionCD.swift in Sources */, 739DFF7B25DC1E3C005BD5C8 /* AuthenticateView.swift in Sources */, 738B1C3325C660140067407B /* ExpenseCD.swift in Sources */, 738B1C4325C6629D0067407B /* ColorExtension.swift in Sources */, @@ -508,6 +511,7 @@ 243DEC7C2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift in Sources */, 738B1C7725C675200067407B /* AddExpenseView.swift in Sources */, 738B1C8025C6763E0067407B /* ExpenseDetailedView.swift in Sources */, + 2463680C284A35CE007FC25F /* EditMonthlyTransactionView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents b/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents index 34726c0..faf84cc 100644 --- a/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents +++ b/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents @@ -11,7 +11,7 @@ - + @@ -22,6 +22,6 @@ - + \ No newline at end of file diff --git a/Expenso/Library/CoreData/MonthlyExpenseCD.swift b/Expenso/Library/CoreData/MonthlyTransactionCD.swift similarity index 68% rename from Expenso/Library/CoreData/MonthlyExpenseCD.swift rename to Expenso/Library/CoreData/MonthlyTransactionCD.swift index ab00e94..425a02e 100644 --- a/Expenso/Library/CoreData/MonthlyExpenseCD.swift +++ b/Expenso/Library/CoreData/MonthlyTransactionCD.swift @@ -8,7 +8,7 @@ import Foundation import CoreData -public class MonthlyExpenseCD: NSManagedObject, Identifiable { +public class MonthlyTransactionCD: NSManagedObject, Identifiable { @NSManaged public var type: String? @NSManaged public var title: String? @NSManaged public var tag: String? @@ -18,9 +18,9 @@ public class MonthlyExpenseCD: NSManagedObject, Identifiable { @NSManaged public var usingDate: Date? } -extension MonthlyExpenseCD { - static func getAllMonthlyExpenseData() -> NSFetchRequest { - let request: NSFetchRequest = MonthlyExpenseCD.fetchRequest() as! NSFetchRequest +extension MonthlyTransactionCD { + static func getAllMonthlyExpenseData() -> NSFetchRequest { + let request: NSFetchRequest = MonthlyTransactionCD.fetchRequest() as! NSFetchRequest request.sortDescriptors = [] return request } diff --git a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift index a1a67f2..6da9cc6 100644 --- a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift +++ b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift @@ -11,7 +11,7 @@ import CoreData class AddExpenseViewModel: ObservableObject { var expenseObj: ExpenseCD? - var monthlyTransactionObj: MonthlyExpenseCD? + var monthlyTransactionObj: MonthlyTransactionCD? @Published var title = "" @Published var amount = "" @@ -32,36 +32,65 @@ class AddExpenseViewModel: ObservableObject { @Published var showAlert = false @Published var closePresenter = false - init(expenseObj: ExpenseCD? = nil) { - - self.expenseObj = expenseObj - self.title = expenseObj?.title ?? "" - if let expenseObj = expenseObj { - self.amount = String(expenseObj.amount) - self.typeTitle = expenseObj.type == TRANS_TYPE_INCOME ? "Income" : "Expense" + init(expenseObj: ExpenseCD? = nil, monthlyTransactionObj: MonthlyTransactionCD? = nil) { + if monthlyTransactionObj != nil { + + self.monthlyTransactionObj = monthlyTransactionObj + self.title = monthlyTransactionObj?.title ?? "" + if let monthlyTransactionObj = monthlyTransactionObj { + self.amount = String(monthlyTransactionObj.amount) + self.typeTitle = monthlyTransactionObj.type == TRANS_TYPE_INCOME ? "Income" : "Expense" + } else { + self.amount = "" + self.typeTitle = "Income" + } + self.occuredOn = monthlyTransactionObj?.usingDate ?? Date() + self.note = monthlyTransactionObj?.note ?? "" + self.tagTitle = getTransTagTitle(transTag: monthlyTransactionObj?.tag ?? TRANS_TAG_TRANSPORT) + self.selectedType = monthlyTransactionObj?.type ?? TRANS_TYPE_INCOME + self.selectedTag = monthlyTransactionObj?.tag ?? TRANS_TAG_TRANSPORT + if let data = monthlyTransactionObj?.imageAttached { + self.imageAttached = UIImage(data: data) + } + + AttachmentHandler.shared.imagePickedBlock = { [weak self] image in + self?.imageUpdated = true + self?.imageAttached = image + } + } else { - self.amount = "" - self.typeTitle = "Income" - } - self.occuredOn = expenseObj?.occuredOn ?? Date() - self.note = expenseObj?.note ?? "" - self.tagTitle = getTransTagTitle(transTag: expenseObj?.tag ?? TRANS_TAG_TRANSPORT) - self.selectedType = expenseObj?.type ?? TRANS_TYPE_INCOME - self.selectedTag = expenseObj?.tag ?? TRANS_TAG_TRANSPORT - if let data = expenseObj?.imageAttached { - self.imageAttached = UIImage(data: data) + + self.expenseObj = expenseObj + self.title = expenseObj?.title ?? "" + if let expenseObj = expenseObj { + self.amount = String(expenseObj.amount) + self.typeTitle = expenseObj.type == TRANS_TYPE_INCOME ? "Income" : "Expense" + } else { + self.amount = "" + self.typeTitle = "Income" + } + self.occuredOn = expenseObj?.occuredOn ?? Date() + self.note = expenseObj?.note ?? "" + self.tagTitle = getTransTagTitle(transTag: expenseObj?.tag ?? TRANS_TAG_TRANSPORT) + self.selectedType = expenseObj?.type ?? TRANS_TYPE_INCOME + self.selectedTag = expenseObj?.tag ?? TRANS_TAG_TRANSPORT + if let data = expenseObj?.imageAttached { + self.imageAttached = UIImage(data: data) + } + + AttachmentHandler.shared.imagePickedBlock = { [weak self] image in + self?.imageUpdated = true + self?.imageAttached = image + } + } - AttachmentHandler.shared.imagePickedBlock = { [weak self] image in - self?.imageUpdated = true - self?.imageAttached = image - } } func getButtText() -> String { - if selectedType == TRANS_TYPE_INCOME { return "\(expenseObj == nil ? "ADD" : "EDIT") INCOME" } - else if selectedType == TRANS_TYPE_EXPENSE { return "\(expenseObj == nil ? "ADD" : "EDIT") EXPENSE" } - else { return "\(expenseObj == nil ? "ADD" : "EDIT") TRANSACTION" } + if selectedType == TRANS_TYPE_INCOME { return "\((expenseObj != nil || monthlyTransactionObj != nil) ? "EDIT" : "ADD") INCOME" } + else if selectedType == TRANS_TYPE_EXPENSE { return "\((expenseObj != nil || monthlyTransactionObj != nil) ? "EDIT" : "ADD") EXPENSE" } + else { return "\((expenseObj != nil || monthlyTransactionObj != nil) ? "EDIT" : "ADD") TRANSACTION" } } func attachImage() { AttachmentHandler.shared.showAttachmentActionSheet() } @@ -143,7 +172,7 @@ class AddExpenseViewModel: ObservableObject { func saveMonthlyTransaction(managedObjectContext: NSManagedObjectContext) { - let expense: MonthlyExpenseCD + let expense: MonthlyTransactionCD let titleStr = title.trimmingCharacters(in: .whitespacesAndNewlines) let amountStr = amount.trimmingCharacters(in: .whitespacesAndNewlines) @@ -187,12 +216,12 @@ class AddExpenseViewModel: ObservableObject { // } } else { - expense = MonthlyExpenseCD(context: managedObjectContext) - expense.usingDate = occuredOn + expense = MonthlyTransactionCD(context: managedObjectContext) // if let image = imageAttached { // expense.imageAttached = image.jpegData(compressionQuality: 1.0) // } } + expense.usingDate = occuredOn expense.type = selectedType expense.title = titleStr expense.tag = selectedTag @@ -212,7 +241,7 @@ class AddExpenseViewModel: ObservableObject { } catch { alertMsg = "\(error)"; showAlert = true } } - func checkAllMonthlyExpenses(managedObjectContext: NSManagedObjectContext, request: NSFetchRequest) { + func checkAllMonthlyExpenses(managedObjectContext: NSManagedObjectContext, request: NSFetchRequest) { do { let allMonthlyExpenses = try? managedObjectContext.fetch(request) if allMonthlyExpenses == nil { @@ -224,10 +253,10 @@ class AddExpenseViewModel: ObservableObject { if expense.usingDate == nil { continue } - if Calendar.current.isDateInToday(expense.usingDate!) { + if Calendar.current.isDateInToday(expense.usingDate!) || Date() > expense.usingDate! { if let date = Calendar.current.date(byAdding: .month, value: 1, to: expense.usingDate!) { expense.usingDate = date - print(expense.usingDate!) + print("date used") let newExpense = ExpenseCD(context: managedObjectContext) newExpense.amount = expense.amount newExpense.createdAt = Date() @@ -241,7 +270,7 @@ class AddExpenseViewModel: ObservableObject { try managedObjectContext.save() } } else { - print("not current date") + print("not current date or later date") } } } catch { diff --git a/Expenso/Screens/Expense/ExpenseView.swift b/Expenso/Screens/Expense/ExpenseView.swift index e92a739..057c72b 100644 --- a/Expenso/Screens/Expense/ExpenseView.swift +++ b/Expenso/Screens/Expense/ExpenseView.swift @@ -63,7 +63,7 @@ struct ExpenseView: View { label: { Image("plus_icon").resizable().frame(width: 32.0, height: 32.0) }) .padding().background(Color.main_color).cornerRadius(35) Button(action: { - AddExpenseViewModel().checkAllMonthlyExpenses(managedObjectContext: managedObjectContext, request: MonthlyExpenseCD.getAllMonthlyExpenseData()) + AddExpenseViewModel().checkAllMonthlyExpenses(managedObjectContext: managedObjectContext, request: MonthlyTransactionCD.getAllMonthlyExpenseData()) }, label: { Text("Test data") }) diff --git a/Expenso/Screens/MonthlyTransactionDetailed/EditMonthlyTransactionView.swift b/Expenso/Screens/MonthlyTransactionDetailed/EditMonthlyTransactionView.swift new file mode 100644 index 0000000..a54d15a --- /dev/null +++ b/Expenso/Screens/MonthlyTransactionDetailed/EditMonthlyTransactionView.swift @@ -0,0 +1,186 @@ +// +// EditMonthlyTransactionView.swift +// Expenso +// +// Created by Hendrik Steen on 03.06.22. +// + +import SwiftUI + +struct EditMonthlyTransactionView: View { + + @Environment(\.presentationMode) var presentationMode: Binding + // CoreData + @Environment(\.managedObjectContext) var managedObjectContext + @State private var confirmDelete = false + @State var showAttachSheet = false + + @StateObject var viewModel: AddExpenseViewModel + + let typeOptions = [ + DropdownOption(key: TRANS_TYPE_INCOME, val: "Income"), + DropdownOption(key: TRANS_TYPE_EXPENSE, val: "Expense") + ] + + let tagOptions = [ + DropdownOption(key: TRANS_TAG_TRANSPORT, val: "Transport"), + DropdownOption(key: TRANS_TAG_FOOD, val: "Food"), + DropdownOption(key: TRANS_TAG_HOUSING, val: "Housing"), + DropdownOption(key: TRANS_TAG_INSURANCE, val: "Insurance"), + DropdownOption(key: TRANS_TAG_MEDICAL, val: "Medical"), + DropdownOption(key: TRANS_TAG_SAVINGS, val: "Savings"), + DropdownOption(key: TRANS_TAG_PERSONAL, val: "Personal"), + DropdownOption(key: TRANS_TAG_ENTERTAINMENT, val: "Entertainment"), + DropdownOption(key: TRANS_TAG_OTHERS, val: "Others"), + DropdownOption(key: TRANS_TAG_UTILITIES, val: "Utilities") + ] + + var body: some View { + NavigationView { + ZStack { + Color.primary_color.edgesIgnoringSafeArea(.all) + + VStack { + + Group { + ToolbarModelView(title: "Edit Transaction", button1Icon: IMAGE_DELETE_ICON) { self.presentationMode.wrappedValue.dismiss() } + button1Method: { self.confirmDelete = true } + }.alert(isPresented: $confirmDelete, + content: { + Alert(title: Text(APP_NAME), message: Text("Are you sure you want to delete this transaction?"), + primaryButton: .destructive(Text("Delete")) { + viewModel.deleteMonthlyTransaction(managedObjectContext: self.managedObjectContext) + }, secondaryButton: Alert.Button.cancel(Text("Cancel"), action: { confirmDelete = false }) + ) + }) + + ScrollView(showsIndicators: false) { + + VStack(spacing: 12) { + + TextField("Title", text: $viewModel.title) + .modifier(InterFont(.regular, size: 16)) + .accentColor(Color.text_primary_color) + .frame(height: 50).padding(.leading, 16) + .background(Color.secondary_color) + .cornerRadius(4) + + TextField("Amount", text: $viewModel.amount) + .modifier(InterFont(.regular, size: 16)) + .accentColor(Color.text_primary_color) + .frame(height: 50).padding(.leading, 16) + .background(Color.secondary_color) + .cornerRadius(4).keyboardType(.decimalPad) + + DropdownButton(shouldShowDropdown: $viewModel.showTypeDrop, displayText: $viewModel.typeTitle, + options: typeOptions, mainColor: Color.text_primary_color, + backgroundColor: Color.secondary_color, cornerRadius: 4, buttonHeight: 50) { key in + let selectedObj = typeOptions.filter({ $0.key == key }).first + if let object = selectedObj { + viewModel.typeTitle = object.val + viewModel.selectedType = key + } + viewModel.showTypeDrop = false + } + + DropdownButton(shouldShowDropdown: $viewModel.showTagDrop, displayText: $viewModel.tagTitle, + options: tagOptions, mainColor: Color.text_primary_color, + backgroundColor: Color.secondary_color, cornerRadius: 4, buttonHeight: 50) { key in + let selectedObj = tagOptions.filter({ $0.key == key }).first + if let object = selectedObj { + viewModel.tagTitle = object.val + viewModel.selectedTag = key + } + viewModel.showTagDrop = false + } + + HStack { + DatePicker("PickerView", selection: $viewModel.occuredOn, + displayedComponents: [.date, .hourAndMinute]).labelsHidden().padding(.leading, 16) + Spacer() + } + .frame(height: 50).frame(maxWidth: .infinity) + .accentColor(Color.text_primary_color) + .background(Color.secondary_color).cornerRadius(4) + + TextField("Note", text: $viewModel.note) + .modifier(InterFont(.regular, size: 16)) + .accentColor(Color.text_primary_color) + .frame(height: 50).padding(.leading, 16) + .background(Color.secondary_color) + .cornerRadius(4) + + Button(action: { viewModel.attachImage() }, label: { + HStack { + Image(systemName: "paperclip") + .font(.system(size: 18.0, weight: .bold)) + .foregroundColor(Color.text_secondary_color) + .padding(.leading, 16) + TextView(text: "Attach an image", type: .button).foregroundColor(Color.text_secondary_color) + Spacer() + } + }) + .frame(height: 50).frame(maxWidth: .infinity) + .background(Color.secondary_color) + .cornerRadius(4) + .actionSheet(isPresented: $showAttachSheet) { + ActionSheet(title: Text("Do you want to remove the attachment?"), buttons: [ + .default(Text("Remove")) { viewModel.removeImage() }, + .cancel() + ]) + } + + if let image = viewModel.imageAttached { + Button(action: { showAttachSheet = true }, label: { + Image(uiImage: image) + .resizable() + .scaledToFill() + .frame(height: 250).frame(maxWidth: .infinity) + .background(Color.secondary_color) + .cornerRadius(4) + }) + } + + Spacer().frame(height: 150) + Spacer() + } + .frame(maxWidth: .infinity).padding(.horizontal, 8) + .alert(isPresented: $viewModel.showAlert, + content: { Alert(title: Text(APP_NAME), message: Text(viewModel.alertMsg), dismissButton: .default(Text("OK"))) }) + } + + }.edgesIgnoringSafeArea(.top) + + VStack { + Spacer() + VStack { + Button(action: { viewModel.saveMonthlyTransaction(managedObjectContext: managedObjectContext) }, label: { + HStack { + Spacer() + TextView(text: "\(viewModel.getButtText()) FOR EVERY MONTH", type: .button).foregroundColor(.white) + Spacer() + } + }) + .padding(.vertical, 12).background(Color.main_color).cornerRadius(8) + }.padding(.bottom, 16).padding(.horizontal, 8) + } + + } + .navigationBarHidden(true) + } + .dismissKeyboardOnTap() + .navigationViewStyle(StackNavigationViewStyle()) + .navigationBarHidden(true) + .navigationBarBackButtonHidden(true) + .onReceive(viewModel.$closePresenter) { close in + if close { self.presentationMode.wrappedValue.dismiss() } + } + } +} + +//struct AddExpenseView_Previews: PreviewProvider { +// static var previews: some View { +// AddExpenseView() +// } +//} + diff --git a/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift b/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift index 824566d..1e0302a 100644 --- a/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift +++ b/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift @@ -18,7 +18,7 @@ struct MonthlyTransactionDetailedView: View { @State private var confirmDelete = false - init(monthlyTransactionObj: MonthlyExpenseCD) { + init(monthlyTransactionObj: MonthlyTransactionCD) { viewModel = MonthlyTransactionDetailedViewModel(monthlyTransactionObj: monthlyTransactionObj) } @@ -64,24 +64,23 @@ struct MonthlyTransactionDetailedView: View { content: { Alert(title: Text(APP_NAME), message: Text("Are you sure you want to delete this transaction?"), primaryButton: .destructive(Text("Delete")) { - viewModel.deleteNote(managedObjectContext: managedObjectContext) + viewModel.deleteMonthlyTransaction(managedObjectContext: managedObjectContext) }, secondaryButton: Alert.Button.cancel(Text("Cancel"), action: { confirmDelete = false }) ) }) }.edgesIgnoringSafeArea(.all) - //TODO: Edit View for Monthly Expense -// VStack { -// Spacer() -// HStack { -// Spacer() -// NavigationLink(destination: EditMonthlyTransactionView(viewModel: AddExpenseViewModel(expenseObj: nil, monthlyTransactionObj: viewModel.monthlyTransactionObj)), label: { -// Image("pencil_icon").resizable().frame(width: 28.0, height: 28.0) -// Text("Edit").modifier(InterFont(.semiBold, size: 18)).foregroundColor(.white) -// }) -// .padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 20)) -// .background(Color.main_color).cornerRadius(25) -// }.padding(24) -// } + VStack { + Spacer() + HStack { + Spacer() + NavigationLink(destination: EditMonthlyTransactionView(viewModel: AddExpenseViewModel(expenseObj: nil, monthlyTransactionObj: viewModel.monthlyTransactionObj)), label: { + Image("pencil_icon").resizable().frame(width: 28.0, height: 28.0) + Text("Edit").modifier(InterFont(.semiBold, size: 18)).foregroundColor(.white) + }) + .padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 20)) + .background(Color.main_color).cornerRadius(25) + }.padding(24) + } } .navigationBarHidden(true) } diff --git a/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift b/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift index f4f691d..d04995a 100644 --- a/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift +++ b/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift @@ -10,17 +10,17 @@ import CoreData class MonthlyTransactionDetailedViewModel: ObservableObject { - @Published var monthlyTransactionObj: MonthlyExpenseCD + @Published var monthlyTransactionObj: MonthlyTransactionCD @Published var alertMsg = String() @Published var showAlert = false @Published var closePresenter = false - init(monthlyTransactionObj: MonthlyExpenseCD) { + init(monthlyTransactionObj: MonthlyTransactionCD) { self.monthlyTransactionObj = monthlyTransactionObj } - func deleteNote(managedObjectContext: NSManagedObjectContext) { + func deleteMonthlyTransaction(managedObjectContext: NSManagedObjectContext) { managedObjectContext.delete(monthlyTransactionObj) do { try managedObjectContext.save(); closePresenter = true diff --git a/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift b/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift index 9edb889..e9ad1c8 100644 --- a/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift +++ b/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift @@ -10,7 +10,7 @@ import SwiftUI struct MonthlyTransactionSettingsView: View { @Environment(\.presentationMode) var presentationMode: Binding - @FetchRequest(fetchRequest: MonthlyExpenseCD.getAllMonthlyExpenseData()) var monthlyTransactions: FetchedResults + @FetchRequest(fetchRequest: MonthlyTransactionCD.getAllMonthlyExpenseData()) var monthlyTransactions: FetchedResults var body: some View { NavigationView { ZStack { @@ -40,7 +40,7 @@ struct MonthlyTransactionSettingsView: View { struct MonthlyTransView: View { - @ObservedObject var monthlyTransactionObj: MonthlyExpenseCD + @ObservedObject var monthlyTransactionObj: MonthlyTransactionCD @AppStorage(UD_EXPENSE_CURRENCY) var CURRENCY: String = "" var body: some View { @@ -62,7 +62,7 @@ struct MonthlyTransView: View { HStack { TextView(text: getTransTagTitle(transTag: monthlyTransactionObj.tag ?? ""), type: .body_2).foregroundColor(Color.text_primary_color) Spacer() - TextView(text: "made on: ", type: .body_2).foregroundColor(Color.text_primary_color) + TextView(text: "occurs on: ", type: .body_2).foregroundColor(Color.text_primary_color) TextView(text: getDateFormatter(date: monthlyTransactionObj.usingDate, format: "MMM dd, yyyy"), type: .body_2).foregroundColor(Color.text_primary_color) } }.padding(.leading, 4) From e927912bfc64a7271aaca31f9a16e4eac6852786 Mon Sep 17 00:00:00 2001 From: comhendrik Date: Thu, 9 Jun 2022 19:15:24 +0200 Subject: [PATCH 6/9] automatic transactions --- Expenso/Screens/Expense/ExpenseView.swift | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Expenso/Screens/Expense/ExpenseView.swift b/Expenso/Screens/Expense/ExpenseView.swift index 057c72b..72ead33 100644 --- a/Expenso/Screens/Expense/ExpenseView.swift +++ b/Expenso/Screens/Expense/ExpenseView.swift @@ -62,11 +62,6 @@ struct ExpenseView: View { NavigationLink(destination: NavigationLazyView(AddExpenseView(viewModel: AddExpenseViewModel())), label: { Image("plus_icon").resizable().frame(width: 32.0, height: 32.0) }) .padding().background(Color.main_color).cornerRadius(35) - Button(action: { - AddExpenseViewModel().checkAllMonthlyExpenses(managedObjectContext: managedObjectContext, request: MonthlyTransactionCD.getAllMonthlyExpenseData()) - }, label: { - Text("Test data") - }) } }.padding() } @@ -75,6 +70,9 @@ struct ExpenseView: View { .navigationViewStyle(StackNavigationViewStyle()) .navigationBarHidden(true) .navigationBarBackButtonHidden(true) + .onAppear() { + AddExpenseViewModel().checkAllMonthlyExpenses(managedObjectContext: managedObjectContext, request: MonthlyTransactionCD.getAllMonthlyExpenseData()) + } } } From 276880f98856973d8ba1e1b1d0d4a55b7258c1ae Mon Sep 17 00:00:00 2001 From: comhendrik Date: Sun, 12 Jun 2022 22:21:59 +0200 Subject: [PATCH 7/9] first changes for one entity with monthly expenses --- Expenso.xcodeproj/project.pbxproj | 50 +---- Expenso/Library/CoreData/ExpenseCD.swift | 18 ++ .../CoreData/MonthlyTransactionCD.swift | 27 --- .../Screens/AddExpense/AddExpenseView.swift | 10 +- .../AddExpense/AddExpenseViewModel.swift | 190 +++--------------- Expenso/Screens/Expense/ExpenseView.swift | 2 +- .../EditMonthlyTransactionView.swift | 186 ----------------- .../MonthlyTransactionDetailedView.swift | 91 --------- .../MonthlyTransactionDetailedViewModel.swift | 43 ---- .../MonthlyTransactionSettingsView.swift | 74 ------- 10 files changed, 66 insertions(+), 625 deletions(-) delete mode 100644 Expenso/Library/CoreData/MonthlyTransactionCD.swift delete mode 100644 Expenso/Screens/MonthlyTransactionDetailed/EditMonthlyTransactionView.swift delete mode 100644 Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift delete mode 100644 Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift delete mode 100644 Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift diff --git a/Expenso.xcodeproj/project.pbxproj b/Expenso.xcodeproj/project.pbxproj index 23ea29d..40c6914 100644 --- a/Expenso.xcodeproj/project.pbxproj +++ b/Expenso.xcodeproj/project.pbxproj @@ -7,17 +7,12 @@ objects = { /* Begin PBXBuildFile section */ - 243DEC7A2848FCC1001281AD /* MonthlyTransactionDetailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243DEC792848FCC1001281AD /* MonthlyTransactionDetailedView.swift */; }; - 243DEC7C2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 243DEC7B2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift */; }; - 2463680C284A35CE007FC25F /* EditMonthlyTransactionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2463680B284A35CE007FC25F /* EditMonthlyTransactionView.swift */; }; - 24AE70432847F81100B2BD49 /* MonthlyTransactionSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24AE70422847F81100B2BD49 /* MonthlyTransactionSettingsView.swift */; }; - 24DF38B62846B7BC006CA05F /* MonthlyTransactionCD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24DF38B52846B7BC006CA05F /* MonthlyTransactionCD.swift */; }; + 24A10DD02856809200C0BA52 /* Expenso.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 24A10DCE2856809200C0BA52 /* Expenso.xcdatamodeld */; }; 736C720A25CFD89900720DEA /* empty-face.json in Resources */ = {isa = PBXBuildFile; fileRef = 736C720925CFD89900720DEA /* empty-face.json */; }; 736C721B25CFE8E200720DEA /* LottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736C721A25CFE8E200720DEA /* LottieView.swift */; }; 738B1C1825C65DFE0067407B /* ExpensoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738B1C1725C65DFE0067407B /* ExpensoApp.swift */; }; 738B1C1C25C65E060067407B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 738B1C1B25C65E060067407B /* Assets.xcassets */; }; 738B1C1F25C65E060067407B /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 738B1C1E25C65E060067407B /* Preview Assets.xcassets */; }; - 738B1C2425C65E060067407B /* Expenso.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 738B1C2225C65E060067407B /* Expenso.xcdatamodeld */; }; 738B1C2F25C65EF70067407B /* Configs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738B1C2E25C65EF70067407B /* Configs.swift */; }; 738B1C3325C660140067407B /* ExpenseCD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738B1C3225C660140067407B /* ExpenseCD.swift */; }; 738B1C3825C661750067407B /* ExpenseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738B1C3725C661750067407B /* ExpenseView.swift */; }; @@ -58,11 +53,7 @@ /* Begin PBXFileReference section */ 1B0E373FB89DD0864398FEE7 /* Pods_Expenso.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Expenso.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 243DEC792848FCC1001281AD /* MonthlyTransactionDetailedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionDetailedView.swift; sourceTree = ""; }; - 243DEC7B2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionDetailedViewModel.swift; sourceTree = ""; }; - 2463680B284A35CE007FC25F /* EditMonthlyTransactionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditMonthlyTransactionView.swift; sourceTree = ""; }; - 24AE70422847F81100B2BD49 /* MonthlyTransactionSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionSettingsView.swift; sourceTree = ""; }; - 24DF38B52846B7BC006CA05F /* MonthlyTransactionCD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionCD.swift; sourceTree = ""; }; + 24A10DCF2856809200C0BA52 /* Expenso.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Expenso.xcdatamodel; sourceTree = ""; }; 2C2D1600DAC62FA04EDCF170 /* Pods-Expenso.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Expenso.debug.xcconfig"; path = "Target Support Files/Pods-Expenso/Pods-Expenso.debug.xcconfig"; sourceTree = ""; }; 736C720925CFD89900720DEA /* empty-face.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "empty-face.json"; sourceTree = ""; }; 736C721A25CFE8E200720DEA /* LottieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieView.swift; sourceTree = ""; }; @@ -70,7 +61,6 @@ 738B1C1725C65DFE0067407B /* ExpensoApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpensoApp.swift; sourceTree = ""; }; 738B1C1B25C65E060067407B /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 738B1C1E25C65E060067407B /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; - 738B1C2325C65E060067407B /* Expenso.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Expenso.xcdatamodel; sourceTree = ""; }; 738B1C2525C65E060067407B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 738B1C2E25C65EF70067407B /* Configs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Configs.swift; sourceTree = ""; }; 738B1C3225C660140067407B /* ExpenseCD.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExpenseCD.swift; sourceTree = ""; }; @@ -122,29 +112,9 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 243DEC762848FB45001281AD /* MonthlyTransactionSettings */ = { - isa = PBXGroup; - children = ( - 24AE70422847F81100B2BD49 /* MonthlyTransactionSettingsView.swift */, - ); - path = MonthlyTransactionSettings; - sourceTree = ""; - }; - 243DEC782848FCAB001281AD /* MonthlyTransactionDetailed */ = { - isa = PBXGroup; - children = ( - 243DEC792848FCC1001281AD /* MonthlyTransactionDetailedView.swift */, - 2463680B284A35CE007FC25F /* EditMonthlyTransactionView.swift */, - 243DEC7B2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift */, - ); - path = MonthlyTransactionDetailed; - sourceTree = ""; - }; 733763A827120DB600AA983A /* Screens */ = { isa = PBXGroup; children = ( - 243DEC782848FCAB001281AD /* MonthlyTransactionDetailed */, - 243DEC762848FB45001281AD /* MonthlyTransactionSettings */, 739DFF7925DC1E23005BD5C8 /* Authenticate */, 738B1C8E25C680C50067407B /* About */, 738B1C8925C67D790067407B /* ExpenseFilter */, @@ -210,7 +180,7 @@ 738B1C1725C65DFE0067407B /* ExpensoApp.swift */, 738B1C1B25C65E060067407B /* Assets.xcassets */, 738B1C2525C65E060067407B /* Info.plist */, - 738B1C2225C65E060067407B /* Expenso.xcdatamodeld */, + 24A10DCE2856809200C0BA52 /* Expenso.xcdatamodeld */, 738B1C1D25C65E060067407B /* Preview Content */, 738B1C2E25C65EF70067407B /* Configs.swift */, ); @@ -229,7 +199,6 @@ isa = PBXGroup; children = ( 738B1C3225C660140067407B /* ExpenseCD.swift */, - 24DF38B52846B7BC006CA05F /* MonthlyTransactionCD.swift */, ); path = CoreData; sourceTree = ""; @@ -481,15 +450,12 @@ files = ( 738B1C3825C661750067407B /* ExpenseView.swift in Sources */, 73998C5525DA5578007D735B /* AttachmentHandler.swift in Sources */, - 243DEC7A2848FCC1001281AD /* MonthlyTransactionDetailedView.swift in Sources */, 738B1C8425C6764B0067407B /* ExpenseDetailedViewModel.swift in Sources */, 738B1C9025C680D20067407B /* AboutView.swift in Sources */, - 24AE70432847F81100B2BD49 /* MonthlyTransactionSettingsView.swift in Sources */, 738B1C4F25C663180067407B /* DateExtension.swift in Sources */, 738B1C5225C6632F0067407B /* HelperMethods.swift in Sources */, - 738B1C2425C65E060067407B /* Expenso.xcdatamodeld in Sources */, + 24A10DD02856809200C0BA52 /* Expenso.xcdatamodeld in Sources */, 738B1C8B25C67D880067407B /* ExpenseFilterView.swift in Sources */, - 24DF38B62846B7BC006CA05F /* MonthlyTransactionCD.swift in Sources */, 739DFF7B25DC1E3C005BD5C8 /* AuthenticateView.swift in Sources */, 738B1C3325C660140067407B /* ExpenseCD.swift in Sources */, 738B1C4325C6629D0067407B /* ColorExtension.swift in Sources */, @@ -508,10 +474,8 @@ 738B1C2F25C65EF70067407B /* Configs.swift in Sources */, 73E688DA25FF32790000462A /* ChartView.swift in Sources */, 736C721B25CFE8E200720DEA /* LottieView.swift in Sources */, - 243DEC7C2848FF55001281AD /* MonthlyTransactionDetailedViewModel.swift in Sources */, 738B1C7725C675200067407B /* AddExpenseView.swift in Sources */, 738B1C8025C6763E0067407B /* ExpenseDetailedView.swift in Sources */, - 2463680C284A35CE007FC25F /* EditMonthlyTransactionView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -706,12 +670,12 @@ /* End XCConfigurationList section */ /* Begin XCVersionGroup section */ - 738B1C2225C65E060067407B /* Expenso.xcdatamodeld */ = { + 24A10DCE2856809200C0BA52 /* Expenso.xcdatamodeld */ = { isa = XCVersionGroup; children = ( - 738B1C2325C65E060067407B /* Expenso.xcdatamodel */, + 24A10DCF2856809200C0BA52 /* Expenso.xcdatamodel */, ); - currentVersion = 738B1C2325C65E060067407B /* Expenso.xcdatamodel */; + currentVersion = 24A10DCF2856809200C0BA52 /* Expenso.xcdatamodel */; path = Expenso.xcdatamodeld; sourceTree = ""; versionGroupType = wrapper.xcdatamodel; diff --git a/Expenso/Library/CoreData/ExpenseCD.swift b/Expenso/Library/CoreData/ExpenseCD.swift index ac81dee..8347b61 100644 --- a/Expenso/Library/CoreData/ExpenseCD.swift +++ b/Expenso/Library/CoreData/ExpenseCD.swift @@ -20,6 +20,12 @@ enum ExpenseCDFilterTime: String { case month } +@objc +public enum Frequency: Int16 { + case onetime + case monthly +} + public class ExpenseCD: NSManagedObject, Identifiable { @NSManaged public var createdAt: Date? @NSManaged public var updatedAt: Date? @@ -30,6 +36,8 @@ public class ExpenseCD: NSManagedObject, Identifiable { @NSManaged public var note: String? @NSManaged public var amount: Double @NSManaged public var imageAttached: Data? + + @NSManaged public var frequencyValue: Frequency } extension ExpenseCD { @@ -50,4 +58,14 @@ extension ExpenseCD { request.sortDescriptors = [sortDescriptor] return request } + + static func sortExpenseDataByFrequency(sortBy: ExpenseCDSort = .occuredOn, frequency: Frequency, ascending: Bool = true) -> NSFetchRequest { + let request: NSFetchRequest = ExpenseCD.fetchRequest() as! NSFetchRequest + let sortDescriptor = NSSortDescriptor(key: sortBy.rawValue, ascending: ascending) + print(frequency.rawValue) + let predicate = NSPredicate(format: "frequencyValue == %i", frequency.rawValue) + request.predicate = predicate + request.sortDescriptors = [sortDescriptor] + return request + } } diff --git a/Expenso/Library/CoreData/MonthlyTransactionCD.swift b/Expenso/Library/CoreData/MonthlyTransactionCD.swift deleted file mode 100644 index 425a02e..0000000 --- a/Expenso/Library/CoreData/MonthlyTransactionCD.swift +++ /dev/null @@ -1,27 +0,0 @@ -// -// MonthlyExpenseCD.swift -// Expenso -// -// Created by Hendrik Steen on 31.05.22. -// - -import Foundation -import CoreData - -public class MonthlyTransactionCD: NSManagedObject, Identifiable { - @NSManaged public var type: String? - @NSManaged public var title: String? - @NSManaged public var tag: String? - @NSManaged public var note: String? - @NSManaged public var amount: Double - @NSManaged public var imageAttached: Data? - @NSManaged public var usingDate: Date? -} - -extension MonthlyTransactionCD { - static func getAllMonthlyExpenseData() -> NSFetchRequest { - let request: NSFetchRequest = MonthlyTransactionCD.fetchRequest() as! NSFetchRequest - request.sortDescriptors = [] - return request - } -} diff --git a/Expenso/Screens/AddExpense/AddExpenseView.swift b/Expenso/Screens/AddExpense/AddExpenseView.swift index 7a96df2..1c7a3ab 100644 --- a/Expenso/Screens/AddExpense/AddExpenseView.swift +++ b/Expenso/Screens/AddExpense/AddExpenseView.swift @@ -114,6 +114,13 @@ struct AddExpenseView: View { .background(Color.secondary_color) .cornerRadius(4) + Toggle("monthly", isOn: $viewModel.monthlyFrequency) + .padding(5) + .accentColor(Color.text_primary_color) + .frame(height: 50).padding(.leading, 16) + .background(Color.secondary_color) + .cornerRadius(4) + Button(action: { viewModel.attachImage() }, label: { HStack { Image(systemName: "paperclip") @@ -134,6 +141,7 @@ struct AddExpenseView: View { ]) } + if let image = viewModel.imageAttached { Button(action: { showAttachSheet = true }, label: { Image(uiImage: image) @@ -146,7 +154,7 @@ struct AddExpenseView: View { } Spacer().frame(height: 150) - Spacer() + //Spacer() } .frame(maxWidth: .infinity).padding(.horizontal, 8) .alert(isPresented: $viewModel.showAlert, diff --git a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift index 6da9cc6..fb8bc9f 100644 --- a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift +++ b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift @@ -11,7 +11,6 @@ import CoreData class AddExpenseViewModel: ObservableObject { var expenseObj: ExpenseCD? - var monthlyTransactionObj: MonthlyTransactionCD? @Published var title = "" @Published var amount = "" @@ -28,61 +27,37 @@ class AddExpenseViewModel: ObservableObject { @Published var imageUpdated = false // When transaction edit, check if attachment is updated? @Published var imageAttached: UIImage? = nil + @Published var monthlyFrequency = false + @Published var alertMsg = String() @Published var showAlert = false @Published var closePresenter = false - init(expenseObj: ExpenseCD? = nil, monthlyTransactionObj: MonthlyTransactionCD? = nil) { - if monthlyTransactionObj != nil { - - self.monthlyTransactionObj = monthlyTransactionObj - self.title = monthlyTransactionObj?.title ?? "" - if let monthlyTransactionObj = monthlyTransactionObj { - self.amount = String(monthlyTransactionObj.amount) - self.typeTitle = monthlyTransactionObj.type == TRANS_TYPE_INCOME ? "Income" : "Expense" - } else { - self.amount = "" - self.typeTitle = "Income" - } - self.occuredOn = monthlyTransactionObj?.usingDate ?? Date() - self.note = monthlyTransactionObj?.note ?? "" - self.tagTitle = getTransTagTitle(transTag: monthlyTransactionObj?.tag ?? TRANS_TAG_TRANSPORT) - self.selectedType = monthlyTransactionObj?.type ?? TRANS_TYPE_INCOME - self.selectedTag = monthlyTransactionObj?.tag ?? TRANS_TAG_TRANSPORT - if let data = monthlyTransactionObj?.imageAttached { - self.imageAttached = UIImage(data: data) - } - - AttachmentHandler.shared.imagePickedBlock = { [weak self] image in - self?.imageUpdated = true - self?.imageAttached = image - } - + init(expenseObj: ExpenseCD? = nil) { + self.expenseObj = expenseObj + self.title = expenseObj?.title ?? "" + if let expenseObj = expenseObj { + self.amount = String(expenseObj.amount) + self.typeTitle = expenseObj.type == TRANS_TYPE_INCOME ? "Income" : "Expense" } else { - - self.expenseObj = expenseObj - self.title = expenseObj?.title ?? "" - if let expenseObj = expenseObj { - self.amount = String(expenseObj.amount) - self.typeTitle = expenseObj.type == TRANS_TYPE_INCOME ? "Income" : "Expense" - } else { - self.amount = "" - self.typeTitle = "Income" - } - self.occuredOn = expenseObj?.occuredOn ?? Date() - self.note = expenseObj?.note ?? "" - self.tagTitle = getTransTagTitle(transTag: expenseObj?.tag ?? TRANS_TAG_TRANSPORT) - self.selectedType = expenseObj?.type ?? TRANS_TYPE_INCOME - self.selectedTag = expenseObj?.tag ?? TRANS_TAG_TRANSPORT - if let data = expenseObj?.imageAttached { - self.imageAttached = UIImage(data: data) - } - - AttachmentHandler.shared.imagePickedBlock = { [weak self] image in - self?.imageUpdated = true - self?.imageAttached = image - } - + self.amount = "" + self.typeTitle = "Income" + } + self.occuredOn = expenseObj?.occuredOn ?? Date() + self.note = expenseObj?.note ?? "" + self.tagTitle = getTransTagTitle(transTag: expenseObj?.tag ?? TRANS_TAG_TRANSPORT) + self.selectedType = expenseObj?.type ?? TRANS_TYPE_INCOME + self.selectedTag = expenseObj?.tag ?? TRANS_TAG_TRANSPORT + if expenseObj?.frequencyValue == .monthly { + monthlyFrequency = true + } + if let data = expenseObj?.imageAttached { + self.imageAttached = UIImage(data: data) + } + + AttachmentHandler.shared.imagePickedBlock = { [weak self] image in + self?.imageUpdated = true + self?.imageAttached = image } } @@ -156,6 +131,11 @@ class AddExpenseViewModel: ObservableObject { expense.occuredOn = occuredOn expense.note = note expense.amount = amount + if monthlyFrequency { + expense.frequencyValue = .monthly + } else { + expense.frequencyValue = .onetime + } do { try managedObjectContext.save() closePresenter = true @@ -170,112 +150,4 @@ class AddExpenseViewModel: ObservableObject { } catch { alertMsg = "\(error)"; showAlert = true } } - - func saveMonthlyTransaction(managedObjectContext: NSManagedObjectContext) { - let expense: MonthlyTransactionCD - let titleStr = title.trimmingCharacters(in: .whitespacesAndNewlines) - let amountStr = amount.trimmingCharacters(in: .whitespacesAndNewlines) - - if titleStr.isEmpty || titleStr == "" { - alertMsg = "Enter Title"; showAlert = true - return - } - if amountStr.isEmpty || amountStr == "" { - alertMsg = "Enter Amount"; showAlert = true - return - } - guard let amount = Double(amountStr) else { - alertMsg = "Enter valid number"; showAlert = true - return - } - guard amount >= 0 else { - alertMsg = "Amount can't be negative"; showAlert = true - return - } - guard amount <= 1000000000 else { - alertMsg = "Enter a smaller amount"; showAlert = true - return - } - - if monthlyTransactionObj != nil { - - expense = monthlyTransactionObj! - -// if let image = imageAttached { -// if imageUpdated { -// if let _ = expense.imageAttached { -// // Delete Previous Image from CoreData -// } -// expense.imageAttached = image.jpegData(compressionQuality: 1.0) -// } -// } else { -// if let _ = expense.imageAttached { -// // Delete Previous Image from CoreData -// } -// expense.imageAttached = nil -// } - - } else { - expense = MonthlyTransactionCD(context: managedObjectContext) -// if let image = imageAttached { -// expense.imageAttached = image.jpegData(compressionQuality: 1.0) -// } - } - expense.usingDate = occuredOn - expense.type = selectedType - expense.title = titleStr - expense.tag = selectedTag - expense.note = note - expense.amount = amount - do { - try managedObjectContext.save() - closePresenter = true - } catch { alertMsg = "\(error)"; showAlert = true } - } - - func deleteMonthlyTransaction(managedObjectContext: NSManagedObjectContext) { - guard let monthlyTransactionObj = monthlyTransactionObj else { return } - managedObjectContext.delete(monthlyTransactionObj) - do { - try managedObjectContext.save(); closePresenter = true - } catch { alertMsg = "\(error)"; showAlert = true } - } - - func checkAllMonthlyExpenses(managedObjectContext: NSManagedObjectContext, request: NSFetchRequest) { - do { - let allMonthlyExpenses = try? managedObjectContext.fetch(request) - if allMonthlyExpenses == nil { - print("error") - return - } - for expense in allMonthlyExpenses! { - print(expense.usingDate ?? Date()) - if expense.usingDate == nil { - continue - } - if Calendar.current.isDateInToday(expense.usingDate!) || Date() > expense.usingDate! { - if let date = Calendar.current.date(byAdding: .month, value: 1, to: expense.usingDate!) { - expense.usingDate = date - print("date used") - let newExpense = ExpenseCD(context: managedObjectContext) - newExpense.amount = expense.amount - newExpense.createdAt = Date() - //newExpense.imageAttached = expense.imageAttached - newExpense.note = expense.note - newExpense.occuredOn = Date() - newExpense.tag = expense.tag - newExpense.title = expense.title - newExpense.type = expense.type - newExpense.updatedAt = Date() - try managedObjectContext.save() - } - } else { - print("not current date or later date") - } - } - } catch { - print(error) - } - } - } diff --git a/Expenso/Screens/Expense/ExpenseView.swift b/Expenso/Screens/Expense/ExpenseView.swift index 72ead33..095c7b4 100644 --- a/Expenso/Screens/Expense/ExpenseView.swift +++ b/Expenso/Screens/Expense/ExpenseView.swift @@ -12,7 +12,7 @@ struct ExpenseView: View { @Environment(\.presentationMode) var presentationMode: Binding // CoreData @Environment(\.managedObjectContext) var managedObjectContext - @FetchRequest(fetchRequest: ExpenseCD.getAllExpenseData(sortBy: ExpenseCDSort.occuredOn, ascending: false)) var expense: FetchedResults + @FetchRequest(fetchRequest: ExpenseCD.sortExpenseDataByFrequency(frequency: Frequency.onetime)) var expense: FetchedResults @State private var filter: ExpenseCDFilterTime = .all @State private var showFilterSheet = false diff --git a/Expenso/Screens/MonthlyTransactionDetailed/EditMonthlyTransactionView.swift b/Expenso/Screens/MonthlyTransactionDetailed/EditMonthlyTransactionView.swift deleted file mode 100644 index a54d15a..0000000 --- a/Expenso/Screens/MonthlyTransactionDetailed/EditMonthlyTransactionView.swift +++ /dev/null @@ -1,186 +0,0 @@ -// -// EditMonthlyTransactionView.swift -// Expenso -// -// Created by Hendrik Steen on 03.06.22. -// - -import SwiftUI - -struct EditMonthlyTransactionView: View { - - @Environment(\.presentationMode) var presentationMode: Binding - // CoreData - @Environment(\.managedObjectContext) var managedObjectContext - @State private var confirmDelete = false - @State var showAttachSheet = false - - @StateObject var viewModel: AddExpenseViewModel - - let typeOptions = [ - DropdownOption(key: TRANS_TYPE_INCOME, val: "Income"), - DropdownOption(key: TRANS_TYPE_EXPENSE, val: "Expense") - ] - - let tagOptions = [ - DropdownOption(key: TRANS_TAG_TRANSPORT, val: "Transport"), - DropdownOption(key: TRANS_TAG_FOOD, val: "Food"), - DropdownOption(key: TRANS_TAG_HOUSING, val: "Housing"), - DropdownOption(key: TRANS_TAG_INSURANCE, val: "Insurance"), - DropdownOption(key: TRANS_TAG_MEDICAL, val: "Medical"), - DropdownOption(key: TRANS_TAG_SAVINGS, val: "Savings"), - DropdownOption(key: TRANS_TAG_PERSONAL, val: "Personal"), - DropdownOption(key: TRANS_TAG_ENTERTAINMENT, val: "Entertainment"), - DropdownOption(key: TRANS_TAG_OTHERS, val: "Others"), - DropdownOption(key: TRANS_TAG_UTILITIES, val: "Utilities") - ] - - var body: some View { - NavigationView { - ZStack { - Color.primary_color.edgesIgnoringSafeArea(.all) - - VStack { - - Group { - ToolbarModelView(title: "Edit Transaction", button1Icon: IMAGE_DELETE_ICON) { self.presentationMode.wrappedValue.dismiss() } - button1Method: { self.confirmDelete = true } - }.alert(isPresented: $confirmDelete, - content: { - Alert(title: Text(APP_NAME), message: Text("Are you sure you want to delete this transaction?"), - primaryButton: .destructive(Text("Delete")) { - viewModel.deleteMonthlyTransaction(managedObjectContext: self.managedObjectContext) - }, secondaryButton: Alert.Button.cancel(Text("Cancel"), action: { confirmDelete = false }) - ) - }) - - ScrollView(showsIndicators: false) { - - VStack(spacing: 12) { - - TextField("Title", text: $viewModel.title) - .modifier(InterFont(.regular, size: 16)) - .accentColor(Color.text_primary_color) - .frame(height: 50).padding(.leading, 16) - .background(Color.secondary_color) - .cornerRadius(4) - - TextField("Amount", text: $viewModel.amount) - .modifier(InterFont(.regular, size: 16)) - .accentColor(Color.text_primary_color) - .frame(height: 50).padding(.leading, 16) - .background(Color.secondary_color) - .cornerRadius(4).keyboardType(.decimalPad) - - DropdownButton(shouldShowDropdown: $viewModel.showTypeDrop, displayText: $viewModel.typeTitle, - options: typeOptions, mainColor: Color.text_primary_color, - backgroundColor: Color.secondary_color, cornerRadius: 4, buttonHeight: 50) { key in - let selectedObj = typeOptions.filter({ $0.key == key }).first - if let object = selectedObj { - viewModel.typeTitle = object.val - viewModel.selectedType = key - } - viewModel.showTypeDrop = false - } - - DropdownButton(shouldShowDropdown: $viewModel.showTagDrop, displayText: $viewModel.tagTitle, - options: tagOptions, mainColor: Color.text_primary_color, - backgroundColor: Color.secondary_color, cornerRadius: 4, buttonHeight: 50) { key in - let selectedObj = tagOptions.filter({ $0.key == key }).first - if let object = selectedObj { - viewModel.tagTitle = object.val - viewModel.selectedTag = key - } - viewModel.showTagDrop = false - } - - HStack { - DatePicker("PickerView", selection: $viewModel.occuredOn, - displayedComponents: [.date, .hourAndMinute]).labelsHidden().padding(.leading, 16) - Spacer() - } - .frame(height: 50).frame(maxWidth: .infinity) - .accentColor(Color.text_primary_color) - .background(Color.secondary_color).cornerRadius(4) - - TextField("Note", text: $viewModel.note) - .modifier(InterFont(.regular, size: 16)) - .accentColor(Color.text_primary_color) - .frame(height: 50).padding(.leading, 16) - .background(Color.secondary_color) - .cornerRadius(4) - - Button(action: { viewModel.attachImage() }, label: { - HStack { - Image(systemName: "paperclip") - .font(.system(size: 18.0, weight: .bold)) - .foregroundColor(Color.text_secondary_color) - .padding(.leading, 16) - TextView(text: "Attach an image", type: .button).foregroundColor(Color.text_secondary_color) - Spacer() - } - }) - .frame(height: 50).frame(maxWidth: .infinity) - .background(Color.secondary_color) - .cornerRadius(4) - .actionSheet(isPresented: $showAttachSheet) { - ActionSheet(title: Text("Do you want to remove the attachment?"), buttons: [ - .default(Text("Remove")) { viewModel.removeImage() }, - .cancel() - ]) - } - - if let image = viewModel.imageAttached { - Button(action: { showAttachSheet = true }, label: { - Image(uiImage: image) - .resizable() - .scaledToFill() - .frame(height: 250).frame(maxWidth: .infinity) - .background(Color.secondary_color) - .cornerRadius(4) - }) - } - - Spacer().frame(height: 150) - Spacer() - } - .frame(maxWidth: .infinity).padding(.horizontal, 8) - .alert(isPresented: $viewModel.showAlert, - content: { Alert(title: Text(APP_NAME), message: Text(viewModel.alertMsg), dismissButton: .default(Text("OK"))) }) - } - - }.edgesIgnoringSafeArea(.top) - - VStack { - Spacer() - VStack { - Button(action: { viewModel.saveMonthlyTransaction(managedObjectContext: managedObjectContext) }, label: { - HStack { - Spacer() - TextView(text: "\(viewModel.getButtText()) FOR EVERY MONTH", type: .button).foregroundColor(.white) - Spacer() - } - }) - .padding(.vertical, 12).background(Color.main_color).cornerRadius(8) - }.padding(.bottom, 16).padding(.horizontal, 8) - } - - } - .navigationBarHidden(true) - } - .dismissKeyboardOnTap() - .navigationViewStyle(StackNavigationViewStyle()) - .navigationBarHidden(true) - .navigationBarBackButtonHidden(true) - .onReceive(viewModel.$closePresenter) { close in - if close { self.presentationMode.wrappedValue.dismiss() } - } - } -} - -//struct AddExpenseView_Previews: PreviewProvider { -// static var previews: some View { -// AddExpenseView() -// } -//} - diff --git a/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift b/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift deleted file mode 100644 index 1e0302a..0000000 --- a/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedView.swift +++ /dev/null @@ -1,91 +0,0 @@ -// -// MonthlyTransactionDetailedView.swift -// Expenso -// -// Created by Hendrik Steen on 02.06.22. -// - -import SwiftUI - -struct MonthlyTransactionDetailedView: View { - - @Environment(\.presentationMode) var presentationMode: Binding - // CoreData - @Environment(\.managedObjectContext) var managedObjectContext - - @ObservedObject private var viewModel: MonthlyTransactionDetailedViewModel - @AppStorage(UD_EXPENSE_CURRENCY) var CURRENCY: String = "" - - @State private var confirmDelete = false - - init(monthlyTransactionObj: MonthlyTransactionCD) { - viewModel = MonthlyTransactionDetailedViewModel(monthlyTransactionObj: monthlyTransactionObj) - } - - var body: some View { - NavigationView { - ZStack { - Color.primary_color.edgesIgnoringSafeArea(.all) - - VStack { - - ToolbarModelView(title: "Details", button1Icon: IMAGE_DELETE_ICON, button2Icon: IMAGE_SHARE_ICON) { self.presentationMode.wrappedValue.dismiss() } - button1Method: { self.confirmDelete = true } - button2Method: { viewModel.shareNote() } - - ScrollView(showsIndicators: false) { - - VStack(spacing: 24) { - ExpenseDetailedListView(title: "Title", description: viewModel.monthlyTransactionObj.title ?? "") - ExpenseDetailedListView(title: "Amount", description: "\(CURRENCY)\(viewModel.monthlyTransactionObj.amount)") - ExpenseDetailedListView(title: "Transaction type", description: viewModel.monthlyTransactionObj.type == TRANS_TYPE_INCOME ? "Income" : "Expense" ) - ExpenseDetailedListView(title: "Tag", description: getTransTagTitle(transTag: viewModel.monthlyTransactionObj.tag ?? "")) - ExpenseDetailedListView(title: "When", description: getDateFormatter(date: viewModel.monthlyTransactionObj.usingDate, format: "EEEE, dd MMM hh:mm a")) - if let note = viewModel.monthlyTransactionObj.note, note != "" { - ExpenseDetailedListView(title: "Note", description: note) - } - if let data = viewModel.monthlyTransactionObj.imageAttached { - VStack(spacing: 8) { - HStack { TextView(text: "Attachment", type: .caption).foregroundColor(Color.init(hex: "828282")); Spacer() } - Image(uiImage: UIImage(data: data)!) - .resizable() - .scaledToFill() - .frame(height: 250).frame(maxWidth: .infinity) - .background(Color.secondary_color) - .cornerRadius(4) - } - } - }.padding(16) - - Spacer().frame(height: 24) - Spacer() - } - .alert(isPresented: $confirmDelete, - content: { - Alert(title: Text(APP_NAME), message: Text("Are you sure you want to delete this transaction?"), - primaryButton: .destructive(Text("Delete")) { - viewModel.deleteMonthlyTransaction(managedObjectContext: managedObjectContext) - }, secondaryButton: Alert.Button.cancel(Text("Cancel"), action: { confirmDelete = false }) - ) - }) - }.edgesIgnoringSafeArea(.all) - VStack { - Spacer() - HStack { - Spacer() - NavigationLink(destination: EditMonthlyTransactionView(viewModel: AddExpenseViewModel(expenseObj: nil, monthlyTransactionObj: viewModel.monthlyTransactionObj)), label: { - Image("pencil_icon").resizable().frame(width: 28.0, height: 28.0) - Text("Edit").modifier(InterFont(.semiBold, size: 18)).foregroundColor(.white) - }) - .padding(EdgeInsets(top: 10, leading: 15, bottom: 10, trailing: 20)) - .background(Color.main_color).cornerRadius(25) - }.padding(24) - } - } - .navigationBarHidden(true) - } - .navigationViewStyle(StackNavigationViewStyle()) - .navigationBarHidden(true) - .navigationBarBackButtonHidden(true) - } -} diff --git a/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift b/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift deleted file mode 100644 index d04995a..0000000 --- a/Expenso/Screens/MonthlyTransactionDetailed/MonthlyTransactionDetailedViewModel.swift +++ /dev/null @@ -1,43 +0,0 @@ -// -// MonthlyTransactionDetailedViewModel.swift -// Expenso -// -// Created by Hendrik Steen on 02.06.22. -// - -import UIKit -import CoreData - -class MonthlyTransactionDetailedViewModel: ObservableObject { - - @Published var monthlyTransactionObj: MonthlyTransactionCD - - @Published var alertMsg = String() - @Published var showAlert = false - @Published var closePresenter = false - - init(monthlyTransactionObj: MonthlyTransactionCD) { - self.monthlyTransactionObj = monthlyTransactionObj - } - - func deleteMonthlyTransaction(managedObjectContext: NSManagedObjectContext) { - managedObjectContext.delete(monthlyTransactionObj) - do { - try managedObjectContext.save(); closePresenter = true - } catch { alertMsg = "\(error)"; showAlert = true } - } - - func shareNote() { - let shareStr = """ - Title: \(monthlyTransactionObj.title ?? "") - Amount: \(UserDefaults.standard.string(forKey: UD_EXPENSE_CURRENCY) ?? "")\(monthlyTransactionObj.amount) - monthlyTransactionObj Category: \(getTransTagTitle(transTag: monthlyTransactionObj.tag ?? "")) - Date to charge: \(getDateFormatter(date: monthlyTransactionObj.usingDate, format: "EEEE, dd MMM hh:mm a")) - Note: \(monthlyTransactionObj.note ?? "") - - \(SHARED_FROM_EXPENSO) - """ - let av = UIActivityViewController(activityItems: [shareStr], applicationActivities: nil) - UIApplication.shared.windows.first?.rootViewController?.present(av, animated: true, completion: nil) - } -} diff --git a/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift b/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift deleted file mode 100644 index e9ad1c8..0000000 --- a/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift +++ /dev/null @@ -1,74 +0,0 @@ -// -// MonthlyTransactionSettingsView.swift -// Expenso -// -// Created by Hendrik Steen on 01.06.22. -// - -import SwiftUI - -struct MonthlyTransactionSettingsView: View { - @Environment(\.presentationMode) var presentationMode: Binding - - @FetchRequest(fetchRequest: MonthlyTransactionCD.getAllMonthlyExpenseData()) var monthlyTransactions: FetchedResults - var body: some View { - NavigationView { - ZStack { - Color.primary_color.edgesIgnoringSafeArea(.all) - - VStack { - ToolbarModelView(title: "Monthly Transaction") { self.presentationMode.wrappedValue.dismiss() } - ScrollView { - ForEach(self.monthlyTransactions) { monthlyTransactionObj in - NavigationLink(destination: MonthlyTransactionDetailedView(monthlyTransactionObj: monthlyTransactionObj), label: { MonthlyTransView(monthlyTransactionObj: monthlyTransactionObj) }) - } - - } - .padding(.horizontal, 8) - }.edgesIgnoringSafeArea(.all) - } - .navigationBarHidden(true) - - } - .navigationViewStyle(StackNavigationViewStyle()) - .navigationBarHidden(true) - .navigationBarBackButtonHidden(true) - - } - -} - -struct MonthlyTransView: View { - - @ObservedObject var monthlyTransactionObj: MonthlyTransactionCD - @AppStorage(UD_EXPENSE_CURRENCY) var CURRENCY: String = "" - - var body: some View { - HStack { - - NavigationLink(destination: NavigationLazyView(ExpenseFilterView(categTag: monthlyTransactionObj.tag)), label: { - Image(getTransTagIcon(transTag: monthlyTransactionObj.tag ?? "")) - .resizable().frame(width: 24, height: 24).padding(16) - .background(Color.primary_color).cornerRadius(4) - }) - - VStack(alignment: .leading, spacing: 6) { - HStack { - TextView(text: monthlyTransactionObj.title ?? "", type: .subtitle_1, lineLimit: 1).foregroundColor(Color.text_primary_color) - Spacer() - TextView(text: "\(monthlyTransactionObj.type == TRANS_TYPE_INCOME ? "+" : "-")\(CURRENCY)\(monthlyTransactionObj.amount)", type: .subtitle_1) - .foregroundColor(monthlyTransactionObj.type == TRANS_TYPE_INCOME ? Color.main_green : Color.main_red) - } - HStack { - TextView(text: getTransTagTitle(transTag: monthlyTransactionObj.tag ?? ""), type: .body_2).foregroundColor(Color.text_primary_color) - Spacer() - TextView(text: "occurs on: ", type: .body_2).foregroundColor(Color.text_primary_color) - TextView(text: getDateFormatter(date: monthlyTransactionObj.usingDate, format: "MMM dd, yyyy"), type: .body_2).foregroundColor(Color.text_primary_color) - } - }.padding(.leading, 4) - - Spacer() - - }.padding(8).background(Color.secondary_color).cornerRadius(4) - } -} From a6eb61e7f804191d8ca662f7716b57407a59aca5 Mon Sep 17 00:00:00 2001 From: comhendrik Date: Mon, 13 Jun 2022 12:49:54 +0200 Subject: [PATCH 8/9] monthly Transactions with one entity --- Expenso.xcodeproj/project.pbxproj | 12 +++++ .../Expenso.xcdatamodel/contents | 13 +----- Expenso/Library/CoreData/ExpenseCD.swift | 8 +++- .../Screens/AddExpense/AddExpenseView.swift | 28 +++++------- .../AddExpense/AddExpenseViewModel.swift | 40 ++++++++++++++--- Expenso/Screens/Expense/ExpenseView.swift | 14 +++--- .../ExpenseDetailed/ExpenseDetailedView.swift | 7 ++- .../ExpenseFilter/ExpenseFilterView.swift | 2 +- .../MonthlyTransactionSettingsView.swift | 44 +++++++++++++++++++ 9 files changed, 124 insertions(+), 44 deletions(-) create mode 100644 Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift diff --git a/Expenso.xcodeproj/project.pbxproj b/Expenso.xcodeproj/project.pbxproj index 40c6914..066a38e 100644 --- a/Expenso.xcodeproj/project.pbxproj +++ b/Expenso.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 24A10DD02856809200C0BA52 /* Expenso.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 24A10DCE2856809200C0BA52 /* Expenso.xcdatamodeld */; }; + 24D9168D285735920025227B /* MonthlyTransactionSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 24D9168C285735920025227B /* MonthlyTransactionSettingsView.swift */; }; 736C720A25CFD89900720DEA /* empty-face.json in Resources */ = {isa = PBXBuildFile; fileRef = 736C720925CFD89900720DEA /* empty-face.json */; }; 736C721B25CFE8E200720DEA /* LottieView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 736C721A25CFE8E200720DEA /* LottieView.swift */; }; 738B1C1825C65DFE0067407B /* ExpensoApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 738B1C1725C65DFE0067407B /* ExpensoApp.swift */; }; @@ -54,6 +55,7 @@ /* Begin PBXFileReference section */ 1B0E373FB89DD0864398FEE7 /* Pods_Expenso.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Expenso.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 24A10DCF2856809200C0BA52 /* Expenso.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Expenso.xcdatamodel; sourceTree = ""; }; + 24D9168C285735920025227B /* MonthlyTransactionSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonthlyTransactionSettingsView.swift; sourceTree = ""; }; 2C2D1600DAC62FA04EDCF170 /* Pods-Expenso.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Expenso.debug.xcconfig"; path = "Target Support Files/Pods-Expenso/Pods-Expenso.debug.xcconfig"; sourceTree = ""; }; 736C720925CFD89900720DEA /* empty-face.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "empty-face.json"; sourceTree = ""; }; 736C721A25CFE8E200720DEA /* LottieView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LottieView.swift; sourceTree = ""; }; @@ -112,9 +114,18 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 24D9168B285735730025227B /* MonthlyTransactionSettings */ = { + isa = PBXGroup; + children = ( + 24D9168C285735920025227B /* MonthlyTransactionSettingsView.swift */, + ); + path = MonthlyTransactionSettings; + sourceTree = ""; + }; 733763A827120DB600AA983A /* Screens */ = { isa = PBXGroup; children = ( + 24D9168B285735730025227B /* MonthlyTransactionSettings */, 739DFF7925DC1E23005BD5C8 /* Authenticate */, 738B1C8E25C680C50067407B /* About */, 738B1C8925C67D790067407B /* ExpenseFilter */, @@ -463,6 +474,7 @@ 738B1C3C25C662580067407B /* Models.swift in Sources */, 753CBDD325F36864005762B8 /* BiometricAuthUtility.swift in Sources */, 738B1C7325C674320067407B /* ExpenseSettingsView.swift in Sources */, + 24D9168D285735920025227B /* MonthlyTransactionSettingsView.swift in Sources */, 75C6B48025F37FD20079BCFC /* AuthenticationViewModel.swift in Sources */, 738B1C4C25C663060067407B /* DismissKeyboard.swift in Sources */, 738B1C4625C662D90067407B /* TextView.swift in Sources */, diff --git a/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents b/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents index faf84cc..9c162fc 100644 --- a/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents +++ b/Expenso/Expenso.xcdatamodeld/Expenso.xcdatamodel/contents @@ -3,6 +3,7 @@ + @@ -11,17 +12,7 @@ - - - - - - - - - - - + \ No newline at end of file diff --git a/Expenso/Library/CoreData/ExpenseCD.swift b/Expenso/Library/CoreData/ExpenseCD.swift index 8347b61..43fccfb 100644 --- a/Expenso/Library/CoreData/ExpenseCD.swift +++ b/Expenso/Library/CoreData/ExpenseCD.swift @@ -37,7 +37,12 @@ public class ExpenseCD: NSManagedObject, Identifiable { @NSManaged public var amount: Double @NSManaged public var imageAttached: Data? - @NSManaged public var frequencyValue: Frequency + @NSManaged public var frequencyValue: Int16 + + var frequency: Frequency { + get { return Frequency.init(rawValue: frequencyValue) ?? .onetime} + set { frequencyValue = newValue.rawValue} + } } extension ExpenseCD { @@ -62,7 +67,6 @@ extension ExpenseCD { static func sortExpenseDataByFrequency(sortBy: ExpenseCDSort = .occuredOn, frequency: Frequency, ascending: Bool = true) -> NSFetchRequest { let request: NSFetchRequest = ExpenseCD.fetchRequest() as! NSFetchRequest let sortDescriptor = NSSortDescriptor(key: sortBy.rawValue, ascending: ascending) - print(frequency.rawValue) let predicate = NSPredicate(format: "frequencyValue == %i", frequency.rawValue) request.predicate = predicate request.sortDescriptors = [sortDescriptor] diff --git a/Expenso/Screens/AddExpense/AddExpenseView.swift b/Expenso/Screens/AddExpense/AddExpenseView.swift index 1c7a3ab..1ab89e2 100644 --- a/Expenso/Screens/AddExpense/AddExpenseView.swift +++ b/Expenso/Screens/AddExpense/AddExpenseView.swift @@ -17,6 +17,8 @@ struct AddExpenseView: View { @StateObject var viewModel: AddExpenseViewModel + var hasToggle: Bool = true + let typeOptions = [ DropdownOption(key: TRANS_TYPE_INCOME, val: "Income"), DropdownOption(key: TRANS_TYPE_EXPENSE, val: "Expense") @@ -114,12 +116,15 @@ struct AddExpenseView: View { .background(Color.secondary_color) .cornerRadius(4) - Toggle("monthly", isOn: $viewModel.monthlyFrequency) - .padding(5) - .accentColor(Color.text_primary_color) - .frame(height: 50).padding(.leading, 16) - .background(Color.secondary_color) - .cornerRadius(4) + if hasToggle { + //MARK: User can define Transaction as monthly when it is created or change it to onetime, when in the Monthly Transaction View. The User is not allowed to change the state from onetime to monthly in the recent transaction list to prevent that the transaction is done more than one time monthly. If it should be monthly the user has to created a new Expense + Toggle("monthly", isOn: $viewModel.monthlyFrequency) + .padding(5) + .accentColor(Color.text_primary_color) + .frame(height: 50).padding(.leading, 16) + .background(Color.secondary_color) + .cornerRadius(4) + } Button(action: { viewModel.attachImage() }, label: { HStack { @@ -154,7 +159,6 @@ struct AddExpenseView: View { } Spacer().frame(height: 150) - //Spacer() } .frame(maxWidth: .infinity).padding(.horizontal, 8) .alert(isPresented: $viewModel.showAlert, @@ -166,16 +170,6 @@ struct AddExpenseView: View { VStack { Spacer() VStack { - if viewModel.expenseObj == nil { - Button(action: { viewModel.saveMonthlyTransaction(managedObjectContext: managedObjectContext) }, label: { - HStack { - Spacer() - TextView(text: "\(viewModel.getButtText()) every Month", type: .button).foregroundColor(.white) - Spacer() - } - }) - .padding(.vertical, 12).background(Color.main_color).cornerRadius(8) - } Button(action: { viewModel.saveTransaction(managedObjectContext: managedObjectContext) }, label: { HStack { Spacer() diff --git a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift index fb8bc9f..31385fb 100644 --- a/Expenso/Screens/AddExpense/AddExpenseViewModel.swift +++ b/Expenso/Screens/AddExpense/AddExpenseViewModel.swift @@ -48,7 +48,7 @@ class AddExpenseViewModel: ObservableObject { self.tagTitle = getTransTagTitle(transTag: expenseObj?.tag ?? TRANS_TAG_TRANSPORT) self.selectedType = expenseObj?.type ?? TRANS_TYPE_INCOME self.selectedTag = expenseObj?.tag ?? TRANS_TAG_TRANSPORT - if expenseObj?.frequencyValue == .monthly { + if expenseObj?.frequency == .monthly { monthlyFrequency = true } if let data = expenseObj?.imageAttached { @@ -63,9 +63,9 @@ class AddExpenseViewModel: ObservableObject { } func getButtText() -> String { - if selectedType == TRANS_TYPE_INCOME { return "\((expenseObj != nil || monthlyTransactionObj != nil) ? "EDIT" : "ADD") INCOME" } - else if selectedType == TRANS_TYPE_EXPENSE { return "\((expenseObj != nil || monthlyTransactionObj != nil) ? "EDIT" : "ADD") EXPENSE" } - else { return "\((expenseObj != nil || monthlyTransactionObj != nil) ? "EDIT" : "ADD") TRANSACTION" } + if selectedType == TRANS_TYPE_INCOME { return "\(expenseObj != nil ? "EDIT" : "ADD") INCOME" } + else if selectedType == TRANS_TYPE_EXPENSE { return "\(expenseObj != nil ? "EDIT" : "ADD") EXPENSE" } + else { return "\(expenseObj != nil ? "EDIT" : "ADD") TRANSACTION" } } func attachImage() { AttachmentHandler.shared.showAttachmentActionSheet() } @@ -132,9 +132,9 @@ class AddExpenseViewModel: ObservableObject { expense.note = note expense.amount = amount if monthlyFrequency { - expense.frequencyValue = .monthly + expense.frequency = .monthly } else { - expense.frequencyValue = .onetime + expense.frequency = .onetime } do { try managedObjectContext.save() @@ -150,4 +150,32 @@ class AddExpenseViewModel: ObservableObject { } catch { alertMsg = "\(error)"; showAlert = true } } + func repeatTransaction(managedObjectContext: NSManagedObjectContext) { + do { + //TODO: Make sure that only the transactions from one month ago are re done. + let request = ExpenseCD.sortExpenseDataByFrequency(frequency: Frequency.monthly) + let monthlyExpenses = try managedObjectContext.fetch(request) + for expense in monthlyExpenses { + if let compareDate = Calendar.current.date(byAdding: .month, value: 1, to: expense.occuredOn ?? Date()) { + if Calendar.current.isDateInToday(compareDate) || compareDate < Date() { + selectedType = expense.type ?? TRANS_TYPE_INCOME + selectedTag = expense.tag ?? TRANS_TAG_OTHERS + title = expense.title ?? "" + occuredOn = compareDate + if let image = imageAttached { + expense.imageAttached = image.jpegData(compressionQuality: 1.0) + } + note = expense.note ?? "" + amount = String(expense.amount) + monthlyFrequency = true + + //change frequency of old object to onetime + expense.frequency = .onetime + self.saveTransaction(managedObjectContext: managedObjectContext) + } + } + } + try managedObjectContext.save(); closePresenter = true + } catch { alertMsg = "\(error)"; showAlert = true; print(error) } + } } diff --git a/Expenso/Screens/Expense/ExpenseView.swift b/Expenso/Screens/Expense/ExpenseView.swift index 095c7b4..eaa19fd 100644 --- a/Expenso/Screens/Expense/ExpenseView.swift +++ b/Expenso/Screens/Expense/ExpenseView.swift @@ -12,7 +12,9 @@ struct ExpenseView: View { @Environment(\.presentationMode) var presentationMode: Binding // CoreData @Environment(\.managedObjectContext) var managedObjectContext - @FetchRequest(fetchRequest: ExpenseCD.sortExpenseDataByFrequency(frequency: Frequency.onetime)) var expense: FetchedResults + + //MARK: Do you still need this Fetch Request I can't see any use case + @FetchRequest(fetchRequest: ExpenseCD.getAllExpenseData(sortBy: ExpenseCDSort.occuredOn, ascending: false)) var expense: FetchedResults @State private var filter: ExpenseCDFilterTime = .all @State private var showFilterSheet = false @@ -62,6 +64,11 @@ struct ExpenseView: View { NavigationLink(destination: NavigationLazyView(AddExpenseView(viewModel: AddExpenseViewModel())), label: { Image("plus_icon").resizable().frame(width: 32.0, height: 32.0) }) .padding().background(Color.main_color).cornerRadius(35) + Button(action: { + AddExpenseViewModel().repeatTransaction(managedObjectContext: managedObjectContext) + }, label: { + Text("Test data") + }) } }.padding() } @@ -70,9 +77,6 @@ struct ExpenseView: View { .navigationViewStyle(StackNavigationViewStyle()) .navigationBarHidden(true) .navigationBarBackButtonHidden(true) - .onAppear() { - AddExpenseViewModel().checkAllMonthlyExpenses(managedObjectContext: managedObjectContext, request: MonthlyTransactionCD.getAllMonthlyExpenseData()) - } } } @@ -139,7 +143,7 @@ struct ExpenseMainView: View { }.padding(4) ForEach(self.fetchRequest.wrappedValue) { expenseObj in - NavigationLink(destination: ExpenseDetailedView(expenseObj: expenseObj), label: { ExpenseTransView(expenseObj: expenseObj) }) + NavigationLink(destination: ExpenseDetailedView(expenseObj: expenseObj, editViewHasToggle: false), label: { ExpenseTransView(expenseObj: expenseObj) }) } } diff --git a/Expenso/Screens/ExpenseDetailed/ExpenseDetailedView.swift b/Expenso/Screens/ExpenseDetailed/ExpenseDetailedView.swift index 8131cfb..701763d 100644 --- a/Expenso/Screens/ExpenseDetailed/ExpenseDetailedView.swift +++ b/Expenso/Screens/ExpenseDetailed/ExpenseDetailedView.swift @@ -18,8 +18,11 @@ struct ExpenseDetailedView: View { @State private var confirmDelete = false - init(expenseObj: ExpenseCD) { + var editViewHasToggle: Bool + + init(expenseObj: ExpenseCD, editViewHasToggle: Bool) { viewModel = ExpenseDetailedViewModel(expenseObj: expenseObj) + self.editViewHasToggle = editViewHasToggle } var body: some View { @@ -74,7 +77,7 @@ struct ExpenseDetailedView: View { Spacer() HStack { Spacer() - NavigationLink(destination: AddExpenseView(viewModel: AddExpenseViewModel(expenseObj: viewModel.expenseObj)), label: { + NavigationLink(destination: AddExpenseView(viewModel: AddExpenseViewModel(expenseObj: viewModel.expenseObj), hasToggle: editViewHasToggle), label: { Image("pencil_icon").resizable().frame(width: 28.0, height: 28.0) Text("Edit").modifier(InterFont(.semiBold, size: 18)).foregroundColor(.white) }) diff --git a/Expenso/Screens/ExpenseFilter/ExpenseFilterView.swift b/Expenso/Screens/ExpenseFilter/ExpenseFilterView.swift index e51e267..34186b6 100644 --- a/Expenso/Screens/ExpenseFilter/ExpenseFilterView.swift +++ b/Expenso/Screens/ExpenseFilter/ExpenseFilterView.swift @@ -168,7 +168,7 @@ struct ExpenseFilterTransList: View { var body: some View { ForEach(self.fetchRequest.wrappedValue) { expenseObj in - NavigationLink(destination: ExpenseDetailedView(expenseObj: expenseObj), label: { ExpenseTransView(expenseObj: expenseObj) }) + NavigationLink(destination: ExpenseDetailedView(expenseObj: expenseObj, editViewHasToggle: false), label: { ExpenseTransView(expenseObj: expenseObj) }) } } } diff --git a/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift b/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift new file mode 100644 index 0000000..d3eff89 --- /dev/null +++ b/Expenso/Screens/MonthlyTransactionSettings/MonthlyTransactionSettingsView.swift @@ -0,0 +1,44 @@ +// +// MonthlyTransactionSettings.swift +// Expenso +// +// Created by Hendrik Steen on 13.06.22. +// + +import SwiftUI + +struct MonthlyTransactionSettingsView: View { + @Environment(\.presentationMode) var presentationMode: Binding + + @FetchRequest(fetchRequest: ExpenseCD.sortExpenseDataByFrequency(frequency: Frequency.monthly)) var monthlyExpenses: FetchedResults + var body: some View { + NavigationView { + ZStack { + Color.primary_color.edgesIgnoringSafeArea(.all) + + VStack { + ToolbarModelView(title: "Monthly Transaction") { self.presentationMode.wrappedValue.dismiss() } + ScrollView { + ForEach(monthlyExpenses) { expenseObj in + NavigationLink(destination: ExpenseDetailedView(expenseObj: expenseObj, editViewHasToggle: true), label: { ExpenseTransView(expenseObj: expenseObj) }) + } + } + .padding(.horizontal, 8) + }.edgesIgnoringSafeArea(.all) + } + .navigationBarHidden(true) + + } + .navigationViewStyle(StackNavigationViewStyle()) + .navigationBarHidden(true) + .navigationBarBackButtonHidden(true) + + } + + } + +struct MonthlyTransactionSettingsView_Previews: PreviewProvider { + static var previews: some View { + MonthlyTransactionSettingsView() + } +} From d0d300c55067a4b934dcf3087bfcf223b055307a Mon Sep 17 00:00:00 2001 From: comhendrik Date: Wed, 15 Jun 2022 19:02:11 +0200 Subject: [PATCH 9/9] automatic transaction for ExpenseCD --- Expenso/Screens/Expense/ExpenseView.swift | 8 +++----- .../Screens/ExpenseDetailed/ExpenseDetailedView.swift | 9 +++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Expenso/Screens/Expense/ExpenseView.swift b/Expenso/Screens/Expense/ExpenseView.swift index eaa19fd..7c75d13 100644 --- a/Expenso/Screens/Expense/ExpenseView.swift +++ b/Expenso/Screens/Expense/ExpenseView.swift @@ -64,11 +64,6 @@ struct ExpenseView: View { NavigationLink(destination: NavigationLazyView(AddExpenseView(viewModel: AddExpenseViewModel())), label: { Image("plus_icon").resizable().frame(width: 32.0, height: 32.0) }) .padding().background(Color.main_color).cornerRadius(35) - Button(action: { - AddExpenseViewModel().repeatTransaction(managedObjectContext: managedObjectContext) - }, label: { - Text("Test data") - }) } }.padding() } @@ -77,6 +72,9 @@ struct ExpenseView: View { .navigationViewStyle(StackNavigationViewStyle()) .navigationBarHidden(true) .navigationBarBackButtonHidden(true) + .onAppear() { + AddExpenseViewModel().repeatTransaction(managedObjectContext: managedObjectContext) + } } } diff --git a/Expenso/Screens/ExpenseDetailed/ExpenseDetailedView.swift b/Expenso/Screens/ExpenseDetailed/ExpenseDetailedView.swift index 701763d..4e69cfa 100644 --- a/Expenso/Screens/ExpenseDetailed/ExpenseDetailedView.swift +++ b/Expenso/Screens/ExpenseDetailed/ExpenseDetailedView.swift @@ -20,9 +20,12 @@ struct ExpenseDetailedView: View { var editViewHasToggle: Bool + let isMonthly: Bool + init(expenseObj: ExpenseCD, editViewHasToggle: Bool) { viewModel = ExpenseDetailedViewModel(expenseObj: expenseObj) self.editViewHasToggle = editViewHasToggle + self.isMonthly = editViewHasToggle } var body: some View { @@ -44,6 +47,12 @@ struct ExpenseDetailedView: View { ExpenseDetailedListView(title: "Transaction type", description: viewModel.expenseObj.type == TRANS_TYPE_INCOME ? "Income" : "Expense" ) ExpenseDetailedListView(title: "Tag", description: getTransTagTitle(transTag: viewModel.expenseObj.tag ?? "")) ExpenseDetailedListView(title: "When", description: getDateFormatter(date: viewModel.expenseObj.occuredOn, format: "EEEE, dd MMM hh:mm a")) + if self.isMonthly { + Text("This transaction was last performed on this date and will be repeated one month in advance unless you change the status to not monthly") + .foregroundColor(.gray) + .fontWeight(.light) + .font(.subheadline) + } if let note = viewModel.expenseObj.note, note != "" { ExpenseDetailedListView(title: "Note", description: note) }