Checking vehicles by VIN

This commit is contained in:
Selim Mustafaev 2025-06-09 20:44:03 +03:00
parent fe46f42fea
commit 0c8fa61601
15 changed files with 218 additions and 71 deletions

View File

@ -26,6 +26,7 @@
7A1E78F62CE900330004B740 /* ReportScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78F52CE900330004B740 /* ReportScreen.swift */; }; 7A1E78F62CE900330004B740 /* ReportScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78F52CE900330004B740 /* ReportScreen.swift */; };
7A1E78F82CE900440004B740 /* ReportViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78F72CE900440004B740 /* ReportViewModel.swift */; }; 7A1E78F82CE900440004B740 /* ReportViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78F72CE900440004B740 /* ReportViewModel.swift */; };
7A1E78FF2CE91A740004B740 /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78FE2CE91A740004B740 /* Vehicle.swift */; }; 7A1E78FF2CE91A740004B740 /* Vehicle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A1E78FE2CE91A740004B740 /* Vehicle.swift */; };
7A2A8F772DEF205A00FC0AE2 /* VehicleNumberType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2A8F762DEF205A00FC0AE2 /* VehicleNumberType.swift */; };
7A2C96122C3B155B00AE46B5 /* NoteAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */; }; 7A2C96122C3B155B00AE46B5 /* NoteAlertModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */; };
7A2E11292CCE395300E5CA17 /* OptionalDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */; }; 7A2E11292CCE395300E5CA17 /* OptionalDatePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */; };
7A2E6FA72C42B3AD00C40DA7 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; }; 7A2E6FA72C42B3AD00C40DA7 /* AutoCatCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7AF6D1EF2677C03B0086EA64 /* AutoCatCore.framework */; };
@ -41,7 +42,6 @@
7A4927D52CCE438600851C01 /* OptionalBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4927D42CCE438600851C01 /* OptionalBinding.swift */; }; 7A4927D52CCE438600851C01 /* OptionalBinding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4927D42CCE438600851C01 /* OptionalBinding.swift */; };
7A4955822D58CCF900912E66 /* HistoryFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4955812D58CCF900912E66 /* HistoryFilter.swift */; }; 7A4955822D58CCF900912E66 /* HistoryFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A4955812D58CCF900912E66 /* HistoryFilter.swift */; };
7A54BFD32D43B95E00176D6D /* DbUpdatePolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A54BFD22D43B95E00176D6D /* DbUpdatePolicy.swift */; }; 7A54BFD32D43B95E00176D6D /* DbUpdatePolicy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A54BFD22D43B95E00176D6D /* DbUpdatePolicy.swift */; };
7A5631CE2DE4B638001070D6 /* VehicleCheckType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5631CD2DE4B638001070D6 /* VehicleCheckType.swift */; };
7A589E0F2D6B6E8E00EF3FBE /* NumberEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A589E0E2D6B6E8E00EF3FBE /* NumberEditView.swift */; }; 7A589E0F2D6B6E8E00EF3FBE /* NumberEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A589E0E2D6B6E8E00EF3FBE /* NumberEditView.swift */; };
7A5911EE2D63226F00EC51BA /* SearchScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5911ED2D63226F00EC51BA /* SearchScreen.swift */; }; 7A5911EE2D63226F00EC51BA /* SearchScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5911ED2D63226F00EC51BA /* SearchScreen.swift */; };
7A5911F02D63266B00EC51BA /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5911EF2D63266B00EC51BA /* SearchViewModel.swift */; }; 7A5911F02D63266B00EC51BA /* SearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A5911EF2D63266B00EC51BA /* SearchViewModel.swift */; };
@ -275,6 +275,7 @@
7A1E78F72CE900440004B740 /* ReportViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportViewModel.swift; sourceTree = "<group>"; }; 7A1E78F72CE900440004B740 /* ReportViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReportViewModel.swift; sourceTree = "<group>"; };
7A1E78FE2CE91A740004B740 /* Vehicle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = "<group>"; }; 7A1E78FE2CE91A740004B740 /* Vehicle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = "<group>"; };
7A27ADF824A09CAD0035F39E /* CocoaError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaError.swift; sourceTree = "<group>"; }; 7A27ADF824A09CAD0035F39E /* CocoaError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CocoaError.swift; sourceTree = "<group>"; };
7A2A8F762DEF205A00FC0AE2 /* VehicleNumberType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleNumberType.swift; sourceTree = "<group>"; };
7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteAlertModifier.swift; sourceTree = "<group>"; }; 7A2C96112C3B155B00AE46B5 /* NoteAlertModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NoteAlertModifier.swift; sourceTree = "<group>"; };
7A2DE69725868AC800A113FC /* VehicleAd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleAd.swift; sourceTree = "<group>"; }; 7A2DE69725868AC800A113FC /* VehicleAd.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleAd.swift; sourceTree = "<group>"; };
7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalDatePicker.swift; sourceTree = "<group>"; }; 7A2E11282CCE395300E5CA17 /* OptionalDatePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalDatePicker.swift; sourceTree = "<group>"; };
@ -293,7 +294,6 @@
7A52AB292580112E002CD910 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; }; 7A52AB292580112E002CD910 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
7A530B7F2401803A00CBFE6E /* Vehicle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = "<group>"; }; 7A530B7F2401803A00CBFE6E /* Vehicle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Vehicle.swift; sourceTree = "<group>"; };
7A54BFD22D43B95E00176D6D /* DbUpdatePolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DbUpdatePolicy.swift; sourceTree = "<group>"; }; 7A54BFD22D43B95E00176D6D /* DbUpdatePolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DbUpdatePolicy.swift; sourceTree = "<group>"; };
7A5631CD2DE4B638001070D6 /* VehicleCheckType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VehicleCheckType.swift; sourceTree = "<group>"; };
7A589E0E2D6B6E8E00EF3FBE /* NumberEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberEditView.swift; sourceTree = "<group>"; }; 7A589E0E2D6B6E8E00EF3FBE /* NumberEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NumberEditView.swift; sourceTree = "<group>"; };
7A5911ED2D63226F00EC51BA /* SearchScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchScreen.swift; sourceTree = "<group>"; }; 7A5911ED2D63226F00EC51BA /* SearchScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchScreen.swift; sourceTree = "<group>"; };
7A5911EF2D63266B00EC51BA /* SearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModel.swift; sourceTree = "<group>"; }; 7A5911EF2D63266B00EC51BA /* SearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewModel.swift; sourceTree = "<group>"; };
@ -915,6 +915,7 @@
children = ( children = (
7AB4E43A2D3D3F4F0006D052 /* VehicleServiceProtocol.swift */, 7AB4E43A2D3D3F4F0006D052 /* VehicleServiceProtocol.swift */,
7AB4E43C2D3D3F7A0006D052 /* VehicleService.swift */, 7AB4E43C2D3D3F7A0006D052 /* VehicleService.swift */,
7A2A8F762DEF205A00FC0AE2 /* VehicleNumberType.swift */,
); );
path = VehicleService; path = VehicleService;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1001,7 +1002,6 @@
7A8652452DE4484700AEBF27 /* ACVinKeyboardView.swift */, 7A8652452DE4484700AEBF27 /* ACVinKeyboardView.swift */,
7AB4902A2D6B1446002F39C6 /* ACKeyboardButton.swift */, 7AB4902A2D6B1446002F39C6 /* ACKeyboardButton.swift */,
7A589E0E2D6B6E8E00EF3FBE /* NumberEditView.swift */, 7A589E0E2D6B6E8E00EF3FBE /* NumberEditView.swift */,
7A5631CD2DE4B638001070D6 /* VehicleCheckType.swift */,
); );
path = NumberEditView; path = NumberEditView;
sourceTree = "<group>"; sourceTree = "<group>";
@ -1337,7 +1337,6 @@
7A06E0AE2C7065C7005731AC /* SettingsViewModel.swift in Sources */, 7A06E0AE2C7065C7005731AC /* SettingsViewModel.swift in Sources */,
7AB4E42C2D397D8E0006D052 /* VehicleCellView.swift in Sources */, 7AB4E42C2D397D8E0006D052 /* VehicleCellView.swift in Sources */,
7A91CE9A2DC2470F00DBA953 /* TextFieldDumbModifier.swift in Sources */, 7A91CE9A2DC2470F00DBA953 /* TextFieldDumbModifier.swift in Sources */,
7A5631CE2DE4B638001070D6 /* VehicleCheckType.swift in Sources */,
7A961C6E2C4C3C9E00CE2211 /* LinkRowView.swift in Sources */, 7A961C6E2C4C3C9E00CE2211 /* LinkRowView.swift in Sources */,
7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */, 7ADF6CA12512244400F237B2 /* MapExt.swift in Sources */,
7AC44B822DB390B900ADC026 /* MainTabScreen.swift in Sources */, 7AC44B822DB390B900ADC026 /* MainTabScreen.swift in Sources */,
@ -1446,6 +1445,7 @@
7A95197B2D80B41600E69883 /* AudioRecordServiceProtocol.swift in Sources */, 7A95197B2D80B41600E69883 /* AudioRecordServiceProtocol.swift in Sources */,
7AF6D21C2677C1680086EA64 /* DebugInfo.swift in Sources */, 7AF6D21C2677C1680086EA64 /* DebugInfo.swift in Sources */,
7ABDA80D2D8721B10083C715 /* Substrings.swift in Sources */, 7ABDA80D2D8721B10083C715 /* Substrings.swift in Sources */,
7A2A8F772DEF205A00FC0AE2 /* VehicleNumberType.swift in Sources */,
7AF6D2142677C1680086EA64 /* VehicleEvent.swift in Sources */, 7AF6D2142677C1680086EA64 /* VehicleEvent.swift in Sources */,
7A64A2102C19E1EB00284124 /* VehicleBrandDto.swift in Sources */, 7A64A2102C19E1EB00284124 /* VehicleBrandDto.swift in Sources */,
7A599C392C18B22900D47C18 /* FbRefreshTokenModel.swift in Sources */, 7A599C392C18B22900D47C18 /* FbRefreshTokenModel.swift in Sources */,

View File

@ -28,7 +28,7 @@ struct HistoryScreen: View {
ForEach(section.elements) { vehicle in ForEach(section.elements) { vehicle in
vehicleCell(vehicle) vehicleCell(vehicle)
.onTapGesture { .onTapGesture {
viewModel.openVehicleDetail(vehicle) viewModel.openVehicleDetail(vehicle, numberType: .plateNumber)
} }
} }
} }

View File

@ -131,11 +131,16 @@ final class HistoryViewModel: ACHudContainer {
} }
} }
func checkVehicle(number: String, type: CheckType, trackLocation: Bool = true) async { func checkVehicle(
number: String,
numberType: VehicleNumberType = .plateNumber,
checkType: CheckType,
trackLocation: Bool = true
) async {
do { do {
hud = .progress hud = .progress
let (vehicle, errors) = switch type { let (vehicle, errors) = switch checkType {
case .normal: try await vehicleService.check(number: number, trackLocation: trackLocation) case .normal: try await vehicleService.check(number: number, numberType: numberType, trackLocation: trackLocation)
case .update: try await vehicleService.updateHistory(number: number) case .update: try await vehicleService.updateHistory(number: number)
case .record(let event): try await vehicleService.checkRecord(number: number, event: event) case .record(let event): try await vehicleService.checkRecord(number: number, event: event)
} }
@ -145,7 +150,7 @@ final class HistoryViewModel: ACHudContainer {
if errors.isEmpty { if errors.isEmpty {
hud = nil hud = nil
if !vehicle.unrecognized { if !vehicle.unrecognized {
openVehicleDetail(vehicle) openVehicleDetail(vehicle, numberType: numberType)
} }
} else { } else {
showErrors(errors) showErrors(errors)
@ -155,19 +160,25 @@ final class HistoryViewModel: ACHudContainer {
} }
} }
func openVehicleDetail(_ vehicle: VehicleDto) { func openVehicleDetail(_ vehicle: VehicleDto, numberType: VehicleNumberType) {
if Device.isIPhone { if Device.isIPhone {
Router.shared.openLocalReport(vehicle: vehicle) switch numberType {
case .plateNumber:
Router.shared.openLocalReport(vehicle: vehicle)
case .vin:
Router.shared.openSharedReport(vehicle: vehicle)
}
} else { } else {
selectedVehicleId = vehicle.id selectedVehicleId = vehicle.id
} }
} }
func checkNewNumber(_ number: String, checkType: VehicleCheckType, enableLocation: Bool) async { func checkNewNumber(_ number: String, numberType: VehicleNumberType, enableLocation: Bool) async {
await checkVehicle( await checkVehicle(
number: number, number: number,
type: .normal, numberType: numberType,
checkType: .normal,
trackLocation: enableLocation trackLocation: enableLocation
) )
} }
@ -181,11 +192,11 @@ final class HistoryViewModel: ACHudContainer {
} }
func updateVehicle(_ vehicle: VehicleDto) async { func updateVehicle(_ vehicle: VehicleDto) async {
await checkVehicle(number: vehicle.getNumber(), type: .update) await checkVehicle(number: vehicle.getNumber(), checkType: .update)
} }
func checkRecord(number: String, event: VehicleEventDto?) async { func checkRecord(number: String, event: VehicleEventDto?) async {
await checkVehicle(number: number, type: .record(event)) await checkVehicle(number: number, checkType: .record(event))
} }
func onVehicleChanged(_ vehicle: VehicleDto) { func onVehicleChanged(_ vehicle: VehicleDto) {

View File

@ -70,14 +70,14 @@ struct MainSplitScreen: View {
} }
var numberEditSheet: some View { var numberEditSheet: some View {
NumberEditView { number, checkType, enableLocation in NumberEditView { number, numberType, enableLocation in
checkNumberSheetOpened = false checkNumberSheetOpened = false
selection = .history selection = .history
Task { Task {
await historyViewModel.checkNewNumber( await historyViewModel.checkNewNumber(
number, number,
checkType: checkType, numberType: numberType,
enableLocation: enableLocation enableLocation: enableLocation
) )
} }

View File

@ -102,14 +102,14 @@ struct MainTabScreen: View {
} }
var numberEditSheet: some View { var numberEditSheet: some View {
NumberEditView { number, checkType, enableLocation in NumberEditView { number, numberType, enableLocation in
checkNumberSheetOpened = false checkNumberSheetOpened = false
selection = .history selection = .history
Task { Task {
await historyViewModel.checkNewNumber( await historyViewModel.checkNewNumber(
number, number,
checkType: checkType, numberType: numberType,
enableLocation: enableLocation enableLocation: enableLocation
) )
} }

View File

@ -11,11 +11,21 @@ import AutoCatCore
struct NumberEditView: View { struct NumberEditView: View {
let onCheck: (String, VehicleCheckType, Bool) -> Void let onCheck: (String, VehicleNumberType, Bool) -> Void
@State var number = PlateNumber("") @State var number = PlateNumber("")
@State var enableLocation: Bool = true @State var enableLocation: Bool = true
@State var checkType: VehicleCheckType = .plateNumber @State var checkType: VehicleNumberType = .plateNumber
@State var vin: String = ""
var checkButtonEnabled: Bool {
switch checkType {
case .plateNumber:
number.isValid
case .vin:
vin.count == 17
}
}
var body: some View { var body: some View {
VStack(spacing: 16) { VStack(spacing: 16) {
@ -23,32 +33,13 @@ struct NumberEditView: View {
.font(.headline) .font(.headline)
HStack(spacing: 16) { HStack(spacing: 16) {
LicensePlateView(number: number, foreground: .primary) numberField
.layoutPriority(1) checkButton
.frame(height: 50)
.fixedSize(horizontal: true, vertical: false)
Button {
onCheck(number.asString(), checkType, enableLocation)
} label: {
Text("Check")
.frame(maxWidth: .infinity)
.foregroundColor(.primary)
.padding(.vertical, 16)
.padding(.horizontal, 12)
.background {
RoundedRectangle(cornerRadius: 6)
.fill(.blue)
}
}
//.layoutPriority(0)
.frame(height: 50)
.opacity(number.isValid ? 1 : 0.5)
} }
.padding(.horizontal, 4) .padding(.horizontal, 4)
HStack(spacing: 16) { HStack(spacing: 16) {
Picker("Location", selection: $enableLocation) { Picker("Location", selection: checkType == .vin ? .constant(false) : $enableLocation) {
Image(systemName: "location") Image(systemName: "location")
.tag(true) .tag(true)
Image(systemName: "location.slash") Image(systemName: "location.slash")
@ -56,9 +47,10 @@ struct NumberEditView: View {
} }
.pickerStyle(.segmented) .pickerStyle(.segmented)
.frame(width: 120) .frame(width: 120)
.disabled(checkType == .vin)
Picker("Check type", selection: $checkType) { Picker("Check type", selection: $checkType) {
ForEach(VehicleCheckType.allCases) { type in ForEach(VehicleNumberType.allCases) { type in
Text(type.title) Text(type.title)
.selectionDisabled(!type.enabled) .selectionDisabled(!type.enabled)
} }
@ -80,14 +72,113 @@ struct NumberEditView: View {
.gesture(DragGesture(), including: .gesture) .gesture(DragGesture(), including: .gesture)
} }
var numberField: some View {
Group {
switch checkType {
case .plateNumber:
LicensePlateView(number: number, foreground: .primary)
.fixedSize(horizontal: true, vertical: false)
case .vin:
Text(vin)
.padding(12)
.frame(height: 50)
.frame(maxWidth: .infinity, alignment: .leading)
.font(.system(size: 20))
.background {
RoundedRectangle(cornerRadius: 6)
.stroke(.gray)
.fill(.background)
}
.contextMenu { pasteboardMenu }
}
}
.frame(height: 50)
.layoutPriority(1)
}
@ViewBuilder
var pasteboardMenu: some View {
if !vin.isEmpty {
Button {
UIPasteboard.general.string = vin
} label: {
Label("Copy", systemImage: "document.on.document")
}
}
if let value = UIPasteboard.general.string {
Button {
vin = value
} label: {
Label("Paste", systemImage: "document.on.clipboard")
}
}
}
var checkButton: some View {
Button {
switch checkType {
case .plateNumber:
onCheck(number.asString(), checkType, enableLocation)
case .vin:
onCheck(vin, .vin, false)
}
} label: {
Text("Check")
.frame(maxWidth: .infinity)
.foregroundColor(.primary)
.padding(.vertical, 16)
.padding(.horizontal, 12)
.background {
RoundedRectangle(cornerRadius: 6)
.fill(.blue)
}
}
//.layoutPriority(0)
.frame(height: 50)
.opacity(checkButtonEnabled ? 1 : 0.5)
.disabled(!checkButtonEnabled)
.fixedSize(horizontal: checkType == .vin, vertical: false)
}
func buttonPressed(type: PNButtonType) { func buttonPressed(type: PNButtonType) {
switch checkType {
case .plateNumber:
plateNumberChanged(type: type)
case .vin:
vinChanged(type: type)
}
}
func plateNumberChanged(type: PNButtonType) {
switch type { switch type {
case .symbol(let s), .vin(let s): case .symbol(let s):
number.insertText(String(s)) number.insertText(String(s))
case .backspace: case .backspace:
number.deleteBackward() number.deleteBackward()
case .done: case .done:
onCheck(number.asString(), checkType, enableLocation) onCheck(number.asString(), .plateNumber, enableLocation)
case .vin:
break
}
}
func vinChanged(type: PNButtonType) {
guard vin.count < 17 else {
return
}
switch type {
case .symbol(let s), .vin(let s):
vin.append(String(s))
case .backspace:
vin.removeLast()
case .done:
onCheck(vin, .vin, false)
} }
} }
} }

View File

@ -188,4 +188,8 @@ extension VehicleDto {
public var needSync: Bool { public var needSync: Bool {
!synchronized && !unrecognized !synchronized && !unrecognized
} }
public var plateNumberComplete: Bool {
number.count >= 8 && !number.contains { $0 == "*" }
}
} }

View File

@ -3,22 +3,22 @@ import RealmSwift
public final class DebugInfo: Object { public final class DebugInfo: Object {
@Persisted public var autocod: DebugInfoEntry! @Persisted public var autocod: DebugInfoEntry?
@Persisted public var vin01vin: DebugInfoEntry! @Persisted public var vin01vin: DebugInfoEntry?
@Persisted public var vin01base: DebugInfoEntry! @Persisted public var vin01base: DebugInfoEntry?
@Persisted public var vin01history: DebugInfoEntry! @Persisted public var vin01history: DebugInfoEntry?
@Persisted public var nomerogram: DebugInfoEntry! @Persisted public var nomerogram: DebugInfoEntry?
} }
extension DebugInfo: DtoConvertible { extension DebugInfo: DtoConvertible {
public var dto: DebugInfoDto { public var dto: DebugInfoDto {
DebugInfoDto(autocod: autocod.dto, DebugInfoDto(autocod: autocod?.dto,
vin01vin: vin01vin.dto, vin01vin: vin01vin?.dto,
vin01base: vin01base.dto, vin01base: vin01base?.dto,
vin01history: vin01history.dto, vin01history: vin01history?.dto,
nomerogram: nomerogram.dto) nomerogram: nomerogram?.dto)
} }
public convenience init(dto: DebugInfoDto) { public convenience init(dto: DebugInfoDto) {

View File

@ -247,13 +247,20 @@ public actor ApiService: ApiServiceProtocol {
return try await makeGetRequest(api: "vehicles", params: params) return try await makeGetRequest(api: "vehicles", params: params)
} }
public func checkVehicle(by number: String, notes: [VehicleNoteDto], events: [VehicleEventDto], force: Bool = false) async throws -> VehicleDto { public func checkVehicle(
by number: String,
numberType: VehicleNumberType,
notes: [VehicleNoteDto],
events: [VehicleEventDto],
force: Bool = false
) async throws -> VehicleDto {
try await refreshFbToken() try await refreshFbToken()
var body = [ var body = [
"number": AnyEncodable(number), "number": AnyEncodable(number),
"forceUpdate": AnyEncodable(force) "forceUpdate": AnyEncodable(force),
"type": AnyEncodable(numberType.value)
] ]
if let token = await settingsService.user.firebaseIdToken { if let token = await settingsService.user.firebaseIdToken {

View File

@ -29,7 +29,13 @@ public protocol ApiServiceProtocol: Sendable {
func getRegions() async throws -> [VehicleRegion] func getRegions() async throws -> [VehicleRegion]
func getYears() async throws -> [Int] func getYears() async throws -> [Int]
func checkVehicle(by number: String, notes: [VehicleNoteDto], events: [VehicleEventDto], force: Bool) async throws -> VehicleDto func checkVehicle(
by number: String,
numberType: VehicleNumberType,
notes: [VehicleNoteDto],
events: [VehicleEventDto],
force: Bool
) async throws -> VehicleDto
func checkVehicleGb(by number: String) async throws -> VehicleDto func checkVehicleGb(by number: String) async throws -> VehicleDto
func getVehicles(with filter: Filter, pageToken: String?, pageSize: Int) async throws -> PagedResponse<VehicleDto> func getVehicles(with filter: Filter, pageToken: String?, pageSize: Int) async throws -> PagedResponse<VehicleDto>

View File

@ -31,7 +31,7 @@ public actor StorageService: StorageServiceProtocol {
realmConfig.inMemoryIdentifier = UUID().uuidString realmConfig.inMemoryIdentifier = UUID().uuidString
} else { } else {
realmConfig = Realm.Configuration( realmConfig = Realm.Configuration(
schemaVersion: 42, schemaVersion: 43,
migrationBlock: { migration, oldSchemaVersion in } migrationBlock: { migration, oldSchemaVersion in }
) )
} }

View File

@ -8,12 +8,12 @@
import Foundation import Foundation
enum VehicleCheckType: Hashable, CaseIterable, Identifiable { public enum VehicleNumberType: Hashable, CaseIterable, Identifiable, Sendable {
case plateNumber case plateNumber
case vin case vin
var title: String { public var title: String {
switch self { switch self {
case .plateNumber: case .plateNumber:
return String(localized: "Plate number") return String(localized: "Plate number")
@ -22,12 +22,19 @@ enum VehicleCheckType: Hashable, CaseIterable, Identifiable {
} }
} }
var enabled: Bool { public var value: String {
switch self {
case .plateNumber: "GRZ"
case .vin: "VIN"
}
}
public var enabled: Bool {
switch self { switch self {
case .plateNumber: true case .plateNumber: true
case .vin: true case .vin: true
} }
} }
var id: Self { self } public var id: Self { self }
} }

View File

@ -33,13 +33,20 @@ extension VehicleService: VehicleServiceProtocol {
func check( func check(
number: String, number: String,
numberType: VehicleNumberType,
forceUpdate: Bool, forceUpdate: Bool,
trackLocation: Bool, trackLocation: Bool,
additionalEvent: VehicleEventDto?, additionalEvent: VehicleEventDto?,
dbUpdatePolicy: DbUpdatePolicy dbUpdatePolicy: DbUpdatePolicy
) async throws -> VehicleWithErrors { ) async throws -> VehicleWithErrors {
var vehicle = (try? await storageService.loadVehicle(number: number)) ?? VehicleDto(number: number) var vehicle = switch numberType {
case .plateNumber:
(try? await storageService.loadVehicle(number: number)) ?? VehicleDto(number: number)
case .vin:
VehicleDto()
}
var errors: [Error] = [] var errors: [Error] = []
let notes = vehicle.notes let notes = vehicle.notes
@ -58,7 +65,13 @@ extension VehicleService: VehicleServiceProtocol {
} }
async let locationTask = trackLocation ? locationService.getRecentLocation() : nil async let locationTask = trackLocation ? locationService.getRecentLocation() : nil
async let vehicleTask = apiService.checkVehicle(by: number, notes: notes, events: events, force: forceUpdate) async let vehicleTask = apiService.checkVehicle(
by: number,
numberType: numberType,
notes: notes,
events: events,
force: forceUpdate
)
do { do {
vehicle = try await vehicleTask vehicle = try await vehicleTask
@ -82,16 +95,20 @@ extension VehicleService: VehicleServiceProtocol {
} }
await locationService.resetLastEvent() await locationService.resetLastEvent()
try await storageService.updateVehicle(dto: vehicle, policy: dbUpdatePolicy)
if vehicle.plateNumberComplete {
try await storageService.updateVehicle(dto: vehicle, policy: dbUpdatePolicy)
}
return VehicleWithErrors(vehicle: vehicle, errors: errors) return VehicleWithErrors(vehicle: vehicle, errors: errors)
} }
public func check(number: String, trackLocation: Bool) async throws -> VehicleWithErrors { public func check(number: String, numberType: VehicleNumberType, trackLocation: Bool) async throws -> VehicleWithErrors {
#if targetEnvironment(macCatalyst) #if targetEnvironment(macCatalyst)
try await check( try await check(
number: number, number: number,
numberType: numberType,
forceUpdate: false, forceUpdate: false,
trackLocation: false, trackLocation: false,
additionalEvent: nil, additionalEvent: nil,
@ -100,6 +117,7 @@ extension VehicleService: VehicleServiceProtocol {
#else #else
try await check( try await check(
number: number, number: number,
numberType: numberType,
forceUpdate: false, forceUpdate: false,
trackLocation: trackLocation, trackLocation: trackLocation,
additionalEvent: nil, additionalEvent: nil,
@ -112,6 +130,7 @@ extension VehicleService: VehicleServiceProtocol {
try await check( try await check(
number: number, number: number,
numberType: .plateNumber,
forceUpdate: true, forceUpdate: true,
trackLocation: false, trackLocation: false,
additionalEvent: nil, additionalEvent: nil,
@ -123,6 +142,7 @@ extension VehicleService: VehicleServiceProtocol {
try await check( try await check(
number: number, number: number,
numberType: .plateNumber,
forceUpdate: true, forceUpdate: true,
trackLocation: false, trackLocation: false,
additionalEvent: nil, additionalEvent: nil,
@ -134,6 +154,7 @@ extension VehicleService: VehicleServiceProtocol {
try await check( try await check(
number: number, number: number,
numberType: .plateNumber,
forceUpdate: false, forceUpdate: false,
trackLocation: false, trackLocation: false,
additionalEvent: event, additionalEvent: event,

View File

@ -11,7 +11,7 @@ import Mockable
@Mockable @Mockable
public protocol VehicleServiceProtocol: Sendable { public protocol VehicleServiceProtocol: Sendable {
func check(number: String, trackLocation: Bool) async throws -> VehicleWithErrors func check(number: String, numberType: VehicleNumberType, trackLocation: Bool) async throws -> VehicleWithErrors
func updateHistory(number: String) async throws -> VehicleWithErrors func updateHistory(number: String) async throws -> VehicleWithErrors
func updateSearch(number: String) async throws -> VehicleWithErrors func updateSearch(number: String) async throws -> VehicleWithErrors
func checkRecord(number: String, event: VehicleEventDto?) async throws -> VehicleWithErrors func checkRecord(number: String, event: VehicleEventDto?) async throws -> VehicleWithErrors

View File

@ -19,8 +19,8 @@ public enum Constants {
public var baseUrl: String { public var baseUrl: String {
#if DEBUG #if DEBUG
//"http://127.0.0.1:3000/" "http://127.0.0.1:3000/"
"https://charon.aliencat.pro:8444/" //"https://charon.aliencat.pro:8444/"
#else #else
rawValue rawValue
#endif #endif