Initial draft
This commit is contained in:
37
lib/bloc/app_bloc.dart
Normal file
37
lib/bloc/app_bloc.dart
Normal file
@ -0,0 +1,37 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/bloc/app_events.dart';
|
||||
import 'package:hl_lieferservice/bloc/app_states.dart';
|
||||
import 'package:hl_lieferservice/repository/config.dart';
|
||||
|
||||
import '../services/erpframe.dart';
|
||||
|
||||
class AppBloc extends Bloc<AppEvents, AppState> {
|
||||
AppBloc() : super(AppInitial()) {
|
||||
on<AppLoadConfig>(_loadConfig);
|
||||
}
|
||||
|
||||
Future<void> _loadConfig(AppLoadConfig event, Emitter<AppState> emit) async {
|
||||
emit(AppConfigLoading());
|
||||
try {
|
||||
final repository = ConfigurationRepository(path: event.path);
|
||||
final configuration = LocalDocuFrameConfiguration.fromJson(
|
||||
json.decode(await rootBundle.loadString("assets/${event.path}")),
|
||||
);
|
||||
|
||||
repository.setDocuFrameConfiguration(configuration);
|
||||
|
||||
emit(
|
||||
AppConfigLoaded(config: await repository.getDocuFrameConfiguration()),
|
||||
);
|
||||
} catch (e) {
|
||||
emit(
|
||||
AppConfigLoadingFailed(
|
||||
message: "Fehler beim Laden der Konfigurationsdatei.",
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
lib/bloc/app_events.dart
Normal file
7
lib/bloc/app_events.dart
Normal file
@ -0,0 +1,7 @@
|
||||
abstract class AppEvents {}
|
||||
|
||||
class AppLoadConfig extends AppEvents {
|
||||
String path;
|
||||
|
||||
AppLoadConfig({required this.path});
|
||||
}
|
||||
16
lib/bloc/app_states.dart
Normal file
16
lib/bloc/app_states.dart
Normal file
@ -0,0 +1,16 @@
|
||||
import '../services/erpframe.dart';
|
||||
|
||||
abstract class AppState {}
|
||||
|
||||
class AppInitial extends AppState {}
|
||||
class AppConfigLoading extends AppState {}
|
||||
class AppConfigLoaded extends AppState {
|
||||
LocalDocuFrameConfiguration config;
|
||||
|
||||
AppConfigLoaded({required this.config});
|
||||
}
|
||||
class AppConfigLoadingFailed extends AppState {
|
||||
String message;
|
||||
|
||||
AppConfigLoadingFailed({required this.message});
|
||||
}
|
||||
20
lib/dto/address.dart
Normal file
20
lib/dto/address.dart
Normal file
@ -0,0 +1,20 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'address.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class AddressDTO {
|
||||
AddressDTO(
|
||||
{required this.streetName,
|
||||
required this.postalCode,
|
||||
required this.city});
|
||||
|
||||
String streetName;
|
||||
String postalCode;
|
||||
String city;
|
||||
|
||||
factory AddressDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$AddressDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$AddressDTOToJson(this);
|
||||
}
|
||||
20
lib/dto/address.g.dart
Normal file
20
lib/dto/address.g.dart
Normal file
@ -0,0 +1,20 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'address.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
AddressDTO _$AddressDTOFromJson(Map<String, dynamic> json) => AddressDTO(
|
||||
streetName: json['street_name'] as String,
|
||||
postalCode: json['postal_code'] as String,
|
||||
city: json['city'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$AddressDTOToJson(AddressDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'street_name': instance.streetName,
|
||||
'postal_code': instance.postalCode,
|
||||
'city': instance.city,
|
||||
};
|
||||
35
lib/dto/article.dart
Normal file
35
lib/dto/article.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'article.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class ArticleDTO {
|
||||
ArticleDTO({
|
||||
required this.name,
|
||||
required this.articleNr,
|
||||
required this.quantity,
|
||||
required this.price,
|
||||
required this.scannable,
|
||||
required this.internalId,
|
||||
required this.scannedRemovedAmount,
|
||||
required this.scannedAmount,
|
||||
required this.removeNoteId,
|
||||
required this.taxRate,
|
||||
});
|
||||
|
||||
String name;
|
||||
String articleNr;
|
||||
String quantity;
|
||||
String price;
|
||||
String taxRate;
|
||||
String internalId;
|
||||
String scannedAmount;
|
||||
String scannedRemovedAmount;
|
||||
String? removeNoteId;
|
||||
bool scannable;
|
||||
|
||||
factory ArticleDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$ArticleDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$ArticleDTOToJson(this);
|
||||
}
|
||||
34
lib/dto/article.g.dart
Normal file
34
lib/dto/article.g.dart
Normal file
@ -0,0 +1,34 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'article.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ArticleDTO _$ArticleDTOFromJson(Map<String, dynamic> json) => ArticleDTO(
|
||||
name: json['name'] as String,
|
||||
articleNr: json['article_nr'] as String,
|
||||
quantity: json['quantity'] as String,
|
||||
price: json['price'] as String,
|
||||
scannable: json['scannable'] as bool,
|
||||
internalId: json['internal_id'] as String,
|
||||
scannedRemovedAmount: json['scanned_removed_amount'] as String,
|
||||
scannedAmount: json['scanned_amount'] as String,
|
||||
removeNoteId: json['remove_note_id'] as String?,
|
||||
taxRate: json['tax_rate'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ArticleDTOToJson(ArticleDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'article_nr': instance.articleNr,
|
||||
'quantity': instance.quantity,
|
||||
'price': instance.price,
|
||||
'tax_rate': instance.taxRate,
|
||||
'internal_id': instance.internalId,
|
||||
'scanned_amount': instance.scannedAmount,
|
||||
'scanned_removed_amount': instance.scannedRemovedAmount,
|
||||
'remove_note_id': instance.removeNoteId,
|
||||
'scannable': instance.scannable,
|
||||
};
|
||||
16
lib/dto/basic_response.dart
Normal file
16
lib/dto/basic_response.dart
Normal file
@ -0,0 +1,16 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'basic_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class BasicResponseDTO {
|
||||
BasicResponseDTO(
|
||||
{required this.succeeded,
|
||||
required this.message});
|
||||
|
||||
final bool succeeded;
|
||||
final String message;
|
||||
|
||||
factory BasicResponseDTO.fromJson(Map<String, dynamic> json) => _$BasicResponseDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$BasicResponseDTOToJson(this);
|
||||
}
|
||||
19
lib/dto/basic_response.g.dart
Normal file
19
lib/dto/basic_response.g.dart
Normal file
@ -0,0 +1,19 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'basic_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
BasicResponseDTO _$BasicResponseDTOFromJson(Map<String, dynamic> json) =>
|
||||
BasicResponseDTO(
|
||||
succeeded: json['succeeded'] as bool,
|
||||
message: json['message'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$BasicResponseDTOToJson(BasicResponseDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'succeeded': instance.succeeded,
|
||||
'message': instance.message,
|
||||
};
|
||||
16
lib/dto/car.dart
Normal file
16
lib/dto/car.dart
Normal file
@ -0,0 +1,16 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'car.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class CarDTO {
|
||||
CarDTO(
|
||||
{required this.id,
|
||||
required this.plate});
|
||||
|
||||
final String id;
|
||||
final String plate;
|
||||
|
||||
factory CarDTO.fromJson(Map<String, dynamic> json) => _$CarDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$CarDTOToJson(this);
|
||||
}
|
||||
15
lib/dto/car.g.dart
Normal file
15
lib/dto/car.g.dart
Normal file
@ -0,0 +1,15 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'car.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
CarDTO _$CarDTOFromJson(Map<String, dynamic> json) =>
|
||||
CarDTO(id: json['id'] as String, plate: json['plate'] as String);
|
||||
|
||||
Map<String, dynamic> _$CarDTOToJson(CarDTO instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'plate': instance.plate,
|
||||
};
|
||||
20
lib/dto/car_add.dart
Normal file
20
lib/dto/car_add.dart
Normal file
@ -0,0 +1,20 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'car_add.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class CarAddDTO {
|
||||
CarAddDTO(
|
||||
{required this.teamId,
|
||||
required this.plate});
|
||||
|
||||
final int teamId;
|
||||
final String plate;
|
||||
|
||||
factory CarAddDTO.fromJson(Map<String, dynamic> json) => _$CarAddDTOFromJson(json);
|
||||
factory CarAddDTO.make(int teamID, String plate) {
|
||||
Map<String, dynamic> data = {"team_id": teamID, "plate": plate};
|
||||
return CarAddDTO.fromJson(data);
|
||||
}
|
||||
Map<dynamic, dynamic> toJson() => _$CarAddDTOToJson(this);
|
||||
}
|
||||
17
lib/dto/car_add.g.dart
Normal file
17
lib/dto/car_add.g.dart
Normal file
@ -0,0 +1,17 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'car_add.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
CarAddDTO _$CarAddDTOFromJson(Map<String, dynamic> json) => CarAddDTO(
|
||||
teamId: (json['team_id'] as num).toInt(),
|
||||
plate: json['plate'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CarAddDTOToJson(CarAddDTO instance) => <String, dynamic>{
|
||||
'team_id': instance.teamId,
|
||||
'plate': instance.plate,
|
||||
};
|
||||
19
lib/dto/car_add_response.dart
Normal file
19
lib/dto/car_add_response.dart
Normal file
@ -0,0 +1,19 @@
|
||||
import 'car.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'car_add_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class CarAddResponseDTO {
|
||||
CarAddResponseDTO(
|
||||
{required this.succeeded,
|
||||
required this.message,
|
||||
required this.car});
|
||||
|
||||
final bool succeeded;
|
||||
final String message;
|
||||
final CarDTO car;
|
||||
|
||||
factory CarAddResponseDTO.fromJson(Map<String, dynamic> json) => _$CarAddResponseDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$CarAddResponseDTOToJson(this);
|
||||
}
|
||||
21
lib/dto/car_add_response.g.dart
Normal file
21
lib/dto/car_add_response.g.dart
Normal file
@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'car_add_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
CarAddResponseDTO _$CarAddResponseDTOFromJson(Map<String, dynamic> json) =>
|
||||
CarAddResponseDTO(
|
||||
succeeded: json['succeeded'] as bool,
|
||||
message: json['message'] as String,
|
||||
car: CarDTO.fromJson(json['car'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CarAddResponseDTOToJson(CarAddResponseDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'succeeded': instance.succeeded,
|
||||
'message': instance.message,
|
||||
'car': instance.car,
|
||||
};
|
||||
19
lib/dto/car_get_response.dart
Normal file
19
lib/dto/car_get_response.dart
Normal file
@ -0,0 +1,19 @@
|
||||
import 'car.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'car_get_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class CarGetResponseDTO {
|
||||
CarGetResponseDTO(
|
||||
{required this.succeeded,
|
||||
required this.message,
|
||||
required this.cars});
|
||||
|
||||
final bool succeeded;
|
||||
final String message;
|
||||
final List<CarDTO>? cars;
|
||||
|
||||
factory CarGetResponseDTO.fromJson(Map<String, dynamic> json) => _$CarGetResponseDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$CarGetResponseDTOToJson(this);
|
||||
}
|
||||
24
lib/dto/car_get_response.g.dart
Normal file
24
lib/dto/car_get_response.g.dart
Normal file
@ -0,0 +1,24 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'car_get_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
CarGetResponseDTO _$CarGetResponseDTOFromJson(Map<String, dynamic> json) =>
|
||||
CarGetResponseDTO(
|
||||
succeeded: json['succeeded'] as bool,
|
||||
message: json['message'] as String,
|
||||
cars:
|
||||
(json['cars'] as List<dynamic>?)
|
||||
?.map((e) => CarDTO.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CarGetResponseDTOToJson(CarGetResponseDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'succeeded': instance.succeeded,
|
||||
'message': instance.message,
|
||||
'cars': instance.cars,
|
||||
};
|
||||
20
lib/dto/contact_person.dart
Normal file
20
lib/dto/contact_person.dart
Normal file
@ -0,0 +1,20 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'contact_person.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class ContactPersonDTO {
|
||||
ContactPersonDTO(
|
||||
{required this.name,
|
||||
required this.salutation,
|
||||
required this.phoneNo,
|
||||
required this.mobileNo});
|
||||
|
||||
String name;
|
||||
String salutation;
|
||||
String phoneNo;
|
||||
String mobileNo;
|
||||
|
||||
factory ContactPersonDTO.fromJson(Map<String, dynamic> json) => _$ContactPersonDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$ContactPersonDTOToJson(this);
|
||||
}
|
||||
23
lib/dto/contact_person.g.dart
Normal file
23
lib/dto/contact_person.g.dart
Normal file
@ -0,0 +1,23 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'contact_person.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ContactPersonDTO _$ContactPersonDTOFromJson(Map<String, dynamic> json) =>
|
||||
ContactPersonDTO(
|
||||
name: json['name'] as String,
|
||||
salutation: json['salutation'] as String,
|
||||
phoneNo: json['phone_no'] as String,
|
||||
mobileNo: json['mobile_no'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ContactPersonDTOToJson(ContactPersonDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'name': instance.name,
|
||||
'salutation': instance.salutation,
|
||||
'phone_no': instance.phoneNo,
|
||||
'mobile_no': instance.mobileNo,
|
||||
};
|
||||
15
lib/dto/customer.dart
Normal file
15
lib/dto/customer.dart
Normal file
@ -0,0 +1,15 @@
|
||||
import 'address.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'customer.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class CustomerDTO {
|
||||
CustomerDTO({required this.name, required this.address});
|
||||
|
||||
String name;
|
||||
AddressDTO address;
|
||||
|
||||
factory CustomerDTO.fromJson(Map<String, dynamic> json) => _$CustomerDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$CustomerDTOToJson(this);
|
||||
}
|
||||
15
lib/dto/customer.g.dart
Normal file
15
lib/dto/customer.g.dart
Normal file
@ -0,0 +1,15 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'customer.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
CustomerDTO _$CustomerDTOFromJson(Map<String, dynamic> json) => CustomerDTO(
|
||||
name: json['name'] as String,
|
||||
address: AddressDTO.fromJson(json['address'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$CustomerDTOToJson(CustomerDTO instance) =>
|
||||
<String, dynamic>{'name': instance.name, 'address': instance.address};
|
||||
83
lib/dto/delivery.dart
Normal file
83
lib/dto/delivery.dart
Normal file
@ -0,0 +1,83 @@
|
||||
import 'article.dart';
|
||||
import 'contact_person.dart';
|
||||
import 'customer.dart';
|
||||
import 'discount.dart';
|
||||
import 'image.dart';
|
||||
import 'note.dart';
|
||||
import 'payment.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'delivery.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DeliveryOptionDTO {
|
||||
DeliveryOptionDTO({
|
||||
required this.numerical,
|
||||
required this.value,
|
||||
required this.display,
|
||||
required this.key,
|
||||
});
|
||||
|
||||
bool numerical;
|
||||
String value;
|
||||
String display;
|
||||
String key;
|
||||
|
||||
factory DeliveryOptionDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$DeliveryOptionDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$DeliveryOptionDTOToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DeliveryDTO {
|
||||
DeliveryDTO({
|
||||
required this.internalReceiptNo,
|
||||
required this.specialAggreements,
|
||||
required this.currency,
|
||||
required this.notes,
|
||||
required this.totalPrice,
|
||||
required this.prepayment,
|
||||
required this.paymentAtDelivery,
|
||||
required this.desiredTime,
|
||||
required this.contactPerson,
|
||||
required this.articles,
|
||||
required this.totalNetValue,
|
||||
required this.totalGrossValue,
|
||||
required this.images,
|
||||
required this.customer,
|
||||
required this.finishedTime,
|
||||
required this.note,
|
||||
required this.state,
|
||||
required this.payment,
|
||||
required this.carId,
|
||||
required this.options,
|
||||
});
|
||||
|
||||
String internalReceiptNo;
|
||||
String? specialAggreements;
|
||||
CustomerDTO customer;
|
||||
String totalPrice;
|
||||
String desiredTime;
|
||||
String totalGrossValue;
|
||||
String totalNetValue;
|
||||
ContactPersonDTO contactPerson;
|
||||
String? currency;
|
||||
List<ArticleDTO> articles;
|
||||
String note;
|
||||
String finishedTime;
|
||||
String carId;
|
||||
String state;
|
||||
String prepayment;
|
||||
String paymentAtDelivery;
|
||||
DiscountDTO? discount;
|
||||
PaymentMethodDTO payment;
|
||||
List<NoteDTO> notes;
|
||||
List<ImageDTO> images;
|
||||
List<DeliveryOptionDTO> options;
|
||||
|
||||
factory DeliveryDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$DeliveryDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$DeliveryDTOToJson(this);
|
||||
}
|
||||
89
lib/dto/delivery.g.dart
Normal file
89
lib/dto/delivery.g.dart
Normal file
@ -0,0 +1,89 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'delivery.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DeliveryOptionDTO _$DeliveryOptionDTOFromJson(Map<String, dynamic> json) =>
|
||||
DeliveryOptionDTO(
|
||||
numerical: json['numerical'] as bool,
|
||||
value: json['value'] as String,
|
||||
display: json['display'] as String,
|
||||
key: json['key'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DeliveryOptionDTOToJson(DeliveryOptionDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'numerical': instance.numerical,
|
||||
'value': instance.value,
|
||||
'display': instance.display,
|
||||
'key': instance.key,
|
||||
};
|
||||
|
||||
DeliveryDTO _$DeliveryDTOFromJson(Map<String, dynamic> json) => DeliveryDTO(
|
||||
internalReceiptNo: json['internal_receipt_no'] as String,
|
||||
specialAggreements: json['special_aggreements'] as String?,
|
||||
currency: json['currency'] as String?,
|
||||
notes:
|
||||
(json['notes'] as List<dynamic>)
|
||||
.map((e) => NoteDTO.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
totalPrice: json['total_price'] as String,
|
||||
prepayment: json['prepayment'] as String,
|
||||
paymentAtDelivery: json['payment_at_delivery'] as String,
|
||||
desiredTime: json['desired_time'] as String,
|
||||
contactPerson: ContactPersonDTO.fromJson(
|
||||
json['contact_person'] as Map<String, dynamic>,
|
||||
),
|
||||
articles:
|
||||
(json['articles'] as List<dynamic>)
|
||||
.map((e) => ArticleDTO.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
totalNetValue: json['total_net_value'] as String,
|
||||
totalGrossValue: json['total_gross_value'] as String,
|
||||
images:
|
||||
(json['images'] as List<dynamic>)
|
||||
.map((e) => ImageDTO.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
customer: CustomerDTO.fromJson(json['customer'] as Map<String, dynamic>),
|
||||
finishedTime: json['finished_time'] as String,
|
||||
note: json['note'] as String,
|
||||
state: json['state'] as String,
|
||||
payment: PaymentMethodDTO.fromJson(json['payment'] as Map<String, dynamic>),
|
||||
carId: json['car_id'] as String,
|
||||
options:
|
||||
(json['options'] as List<dynamic>)
|
||||
.map((e) => DeliveryOptionDTO.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
)
|
||||
..discount =
|
||||
json['discount'] == null
|
||||
? null
|
||||
: DiscountDTO.fromJson(json['discount'] as Map<String, dynamic>);
|
||||
|
||||
Map<String, dynamic> _$DeliveryDTOToJson(DeliveryDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'internal_receipt_no': instance.internalReceiptNo,
|
||||
'special_aggreements': instance.specialAggreements,
|
||||
'customer': instance.customer,
|
||||
'total_price': instance.totalPrice,
|
||||
'desired_time': instance.desiredTime,
|
||||
'total_gross_value': instance.totalGrossValue,
|
||||
'total_net_value': instance.totalNetValue,
|
||||
'contact_person': instance.contactPerson,
|
||||
'currency': instance.currency,
|
||||
'articles': instance.articles,
|
||||
'note': instance.note,
|
||||
'finished_time': instance.finishedTime,
|
||||
'car_id': instance.carId,
|
||||
'state': instance.state,
|
||||
'prepayment': instance.prepayment,
|
||||
'payment_at_delivery': instance.paymentAtDelivery,
|
||||
'discount': instance.discount,
|
||||
'payment': instance.payment,
|
||||
'notes': instance.notes,
|
||||
'images': instance.images,
|
||||
'options': instance.options,
|
||||
};
|
||||
22
lib/dto/delivery_response.dart
Normal file
22
lib/dto/delivery_response.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'delivery.dart';
|
||||
import 'driver.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'delivery_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DeliveryResponseDTO {
|
||||
DeliveryResponseDTO(
|
||||
{required this.deliveries,
|
||||
required this.driver,
|
||||
required this.discountArticleNumber});
|
||||
|
||||
List<DeliveryDTO> deliveries;
|
||||
DriverDTO driver;
|
||||
String discountArticleNumber;
|
||||
|
||||
factory DeliveryResponseDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$DeliveryResponseDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$DeliveryResponseDTOToJson(this);
|
||||
}
|
||||
25
lib/dto/delivery_response.g.dart
Normal file
25
lib/dto/delivery_response.g.dart
Normal file
@ -0,0 +1,25 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'delivery_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DeliveryResponseDTO _$DeliveryResponseDTOFromJson(Map<String, dynamic> json) =>
|
||||
DeliveryResponseDTO(
|
||||
deliveries:
|
||||
(json['deliveries'] as List<dynamic>)
|
||||
.map((e) => DeliveryDTO.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
driver: DriverDTO.fromJson(json['driver'] as Map<String, dynamic>),
|
||||
discountArticleNumber: json['discount_article_number'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DeliveryResponseDTOToJson(
|
||||
DeliveryResponseDTO instance,
|
||||
) => <String, dynamic>{
|
||||
'deliveries': instance.deliveries,
|
||||
'driver': instance.driver,
|
||||
'discount_article_number': instance.discountArticleNumber,
|
||||
};
|
||||
55
lib/dto/delivery_update.dart
Normal file
55
lib/dto/delivery_update.dart
Normal file
@ -0,0 +1,55 @@
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'delivery_update.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DeliveryUpdateDTO {
|
||||
DeliveryUpdateDTO({
|
||||
required this.deliveryId,
|
||||
this.note,
|
||||
this.finishedDate,
|
||||
this.discount,
|
||||
this.selectedPaymentMethodId,
|
||||
this.state,
|
||||
this.carId,
|
||||
});
|
||||
|
||||
String deliveryId;
|
||||
String? note;
|
||||
String? finishedDate;
|
||||
String? state;
|
||||
int? carId;
|
||||
String? selectedPaymentMethodId;
|
||||
double? discount;
|
||||
|
||||
factory DeliveryUpdateDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$DeliveryUpdateDTOFromJson(json);
|
||||
|
||||
factory DeliveryUpdateDTO.fromEntity(Delivery delivery) {
|
||||
String state = "";
|
||||
|
||||
switch (delivery.state) {
|
||||
case DeliveryState.finished:
|
||||
state = "geliefert";
|
||||
break;
|
||||
case DeliveryState.ongoing:
|
||||
state = "laufend";
|
||||
break;
|
||||
case DeliveryState.onhold:
|
||||
state = "vertagt";
|
||||
break;
|
||||
case DeliveryState.canceled:
|
||||
state = "abgebrochen";
|
||||
break;
|
||||
}
|
||||
|
||||
return DeliveryUpdateDTO(
|
||||
deliveryId: delivery.id,
|
||||
state: state,
|
||||
carId: delivery.carId,
|
||||
);
|
||||
}
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$DeliveryUpdateDTOToJson(this);
|
||||
}
|
||||
29
lib/dto/delivery_update.g.dart
Normal file
29
lib/dto/delivery_update.g.dart
Normal file
@ -0,0 +1,29 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'delivery_update.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DeliveryUpdateDTO _$DeliveryUpdateDTOFromJson(Map<String, dynamic> json) =>
|
||||
DeliveryUpdateDTO(
|
||||
deliveryId: json['delivery_id'] as String,
|
||||
note: json['note'] as String?,
|
||||
finishedDate: json['finished_date'] as String?,
|
||||
discount: (json['discount'] as num?)?.toDouble(),
|
||||
selectedPaymentMethodId: json['selected_payment_method_id'] as String?,
|
||||
state: json['state'] as String?,
|
||||
carId: (json['car_id'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DeliveryUpdateDTOToJson(DeliveryUpdateDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'delivery_id': instance.deliveryId,
|
||||
'note': instance.note,
|
||||
'finished_date': instance.finishedDate,
|
||||
'state': instance.state,
|
||||
'car_id': instance.carId,
|
||||
'selected_payment_method_id': instance.selectedPaymentMethodId,
|
||||
'discount': instance.discount,
|
||||
};
|
||||
17
lib/dto/delivery_update_response.dart
Normal file
17
lib/dto/delivery_update_response.dart
Normal file
@ -0,0 +1,17 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'delivery_update_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DeliveryUpdateResponseDTO {
|
||||
DeliveryUpdateResponseDTO(
|
||||
{required this.message, required this.code});
|
||||
|
||||
final String code;
|
||||
final String message;
|
||||
|
||||
factory DeliveryUpdateResponseDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$DeliveryUpdateResponseDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$DeliveryUpdateResponseDTOToJson(this);
|
||||
}
|
||||
18
lib/dto/delivery_update_response.g.dart
Normal file
18
lib/dto/delivery_update_response.g.dart
Normal file
@ -0,0 +1,18 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'delivery_update_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DeliveryUpdateResponseDTO _$DeliveryUpdateResponseDTOFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => DeliveryUpdateResponseDTO(
|
||||
message: json['message'] as String,
|
||||
code: json['code'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DeliveryUpdateResponseDTOToJson(
|
||||
DeliveryUpdateResponseDTO instance,
|
||||
) => <String, dynamic>{'code': instance.code, 'message': instance.message};
|
||||
15
lib/dto/discount.dart
Normal file
15
lib/dto/discount.dart
Normal file
@ -0,0 +1,15 @@
|
||||
import 'article.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'discount.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DiscountDTO {
|
||||
DiscountDTO({required this.note, required this.noteId, required this.article});
|
||||
String? note;
|
||||
String? noteId;
|
||||
ArticleDTO article;
|
||||
|
||||
factory DiscountDTO.fromJson(Map<String, dynamic> json) => _$DiscountDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$DiscountDTOToJson(this);
|
||||
}
|
||||
20
lib/dto/discount.g.dart
Normal file
20
lib/dto/discount.g.dart
Normal file
@ -0,0 +1,20 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'discount.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DiscountDTO _$DiscountDTOFromJson(Map<String, dynamic> json) => DiscountDTO(
|
||||
note: json['note'] as String?,
|
||||
noteId: json['note_id'] as String?,
|
||||
article: ArticleDTO.fromJson(json['article'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DiscountDTOToJson(DiscountDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'note': instance.note,
|
||||
'note_id': instance.noteId,
|
||||
'article': instance.article,
|
||||
};
|
||||
18
lib/dto/discount_add.dart
Normal file
18
lib/dto/discount_add.dart
Normal file
@ -0,0 +1,18 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'discount_add.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DiscountAddDTO {
|
||||
DiscountAddDTO(
|
||||
{required this.note, required this.deliveryId, required this.discount});
|
||||
|
||||
String note;
|
||||
String deliveryId;
|
||||
int discount;
|
||||
|
||||
factory DiscountAddDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$DiscountAddDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$DiscountAddDTOToJson(this);
|
||||
}
|
||||
21
lib/dto/discount_add.g.dart
Normal file
21
lib/dto/discount_add.g.dart
Normal file
@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'discount_add.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DiscountAddDTO _$DiscountAddDTOFromJson(Map<String, dynamic> json) =>
|
||||
DiscountAddDTO(
|
||||
note: json['note'] as String,
|
||||
deliveryId: json['delivery_id'] as String,
|
||||
discount: (json['discount'] as num).toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DiscountAddDTOToJson(DiscountAddDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'note': instance.note,
|
||||
'delivery_id': instance.deliveryId,
|
||||
'discount': instance.discount,
|
||||
};
|
||||
63
lib/dto/discount_add_response.dart
Normal file
63
lib/dto/discount_add_response.dart
Normal file
@ -0,0 +1,63 @@
|
||||
import 'article.dart';
|
||||
import 'basic_response.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'discount_add_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class PriceInformation {
|
||||
PriceInformation({required this.net, required this.gross});
|
||||
|
||||
double net;
|
||||
double gross;
|
||||
|
||||
factory PriceInformation.fromJson(Map<String, dynamic> json) =>
|
||||
_$PriceInformationFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$PriceInformationToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class NoteInformation {
|
||||
NoteInformation({required this.rowId, required this.noteDescription});
|
||||
|
||||
String rowId;
|
||||
String noteDescription;
|
||||
|
||||
factory NoteInformation.fromJson(Map<String, dynamic> json) =>
|
||||
_$NoteInformationFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$NoteInformationToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class UpdatedValues {
|
||||
UpdatedValues(
|
||||
{required this.discount,
|
||||
required this.receipt,
|
||||
required this.article,
|
||||
required this.note});
|
||||
|
||||
PriceInformation discount;
|
||||
PriceInformation receipt;
|
||||
NoteInformation note;
|
||||
ArticleDTO article;
|
||||
|
||||
factory UpdatedValues.fromJson(Map<String, dynamic> json) =>
|
||||
_$UpdatedValuesFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$UpdatedValuesToJson(this);
|
||||
}
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DiscountAddResponseDTO extends BasicResponseDTO {
|
||||
DiscountAddResponseDTO(
|
||||
{required this.values, required super.succeeded, required super.message});
|
||||
|
||||
UpdatedValues values;
|
||||
|
||||
factory DiscountAddResponseDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$DiscountAddResponseDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$DiscountAddResponseDTOToJson(this);
|
||||
}
|
||||
61
lib/dto/discount_add_response.g.dart
Normal file
61
lib/dto/discount_add_response.g.dart
Normal file
@ -0,0 +1,61 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'discount_add_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
PriceInformation _$PriceInformationFromJson(Map<String, dynamic> json) =>
|
||||
PriceInformation(
|
||||
net: (json['net'] as num).toDouble(),
|
||||
gross: (json['gross'] as num).toDouble(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PriceInformationToJson(PriceInformation instance) =>
|
||||
<String, dynamic>{'net': instance.net, 'gross': instance.gross};
|
||||
|
||||
NoteInformation _$NoteInformationFromJson(Map<String, dynamic> json) =>
|
||||
NoteInformation(
|
||||
rowId: json['row_id'] as String,
|
||||
noteDescription: json['note_description'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NoteInformationToJson(NoteInformation instance) =>
|
||||
<String, dynamic>{
|
||||
'row_id': instance.rowId,
|
||||
'note_description': instance.noteDescription,
|
||||
};
|
||||
|
||||
UpdatedValues _$UpdatedValuesFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => UpdatedValues(
|
||||
discount: PriceInformation.fromJson(json['discount'] as Map<String, dynamic>),
|
||||
receipt: PriceInformation.fromJson(json['receipt'] as Map<String, dynamic>),
|
||||
article: ArticleDTO.fromJson(json['article'] as Map<String, dynamic>),
|
||||
note: NoteInformation.fromJson(json['note'] as Map<String, dynamic>),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$UpdatedValuesToJson(UpdatedValues instance) =>
|
||||
<String, dynamic>{
|
||||
'discount': instance.discount,
|
||||
'receipt': instance.receipt,
|
||||
'note': instance.note,
|
||||
'article': instance.article,
|
||||
};
|
||||
|
||||
DiscountAddResponseDTO _$DiscountAddResponseDTOFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => DiscountAddResponseDTO(
|
||||
values: UpdatedValues.fromJson(json['values'] as Map<String, dynamic>),
|
||||
succeeded: json['succeeded'] as bool,
|
||||
message: json['message'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DiscountAddResponseDTOToJson(
|
||||
DiscountAddResponseDTO instance,
|
||||
) => <String, dynamic>{
|
||||
'succeeded': instance.succeeded,
|
||||
'message': instance.message,
|
||||
'values': instance.values,
|
||||
};
|
||||
15
lib/dto/discount_remove.dart
Normal file
15
lib/dto/discount_remove.dart
Normal file
@ -0,0 +1,15 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'discount_remove.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DiscountRemoveDTO {
|
||||
DiscountRemoveDTO(
|
||||
{required this.deliveryId});
|
||||
String deliveryId;
|
||||
|
||||
factory DiscountRemoveDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$DiscountRemoveDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$DiscountRemoveDTOToJson(this);
|
||||
}
|
||||
13
lib/dto/discount_remove.g.dart
Normal file
13
lib/dto/discount_remove.g.dart
Normal file
@ -0,0 +1,13 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'discount_remove.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DiscountRemoveDTO _$DiscountRemoveDTOFromJson(Map<String, dynamic> json) =>
|
||||
DiscountRemoveDTO(deliveryId: json['delivery_id'] as String);
|
||||
|
||||
Map<String, dynamic> _$DiscountRemoveDTOToJson(DiscountRemoveDTO instance) =>
|
||||
<String, dynamic>{'delivery_id': instance.deliveryId};
|
||||
22
lib/dto/discount_remove_response.dart
Normal file
22
lib/dto/discount_remove_response.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'basic_response.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
import 'discount_add_response.dart';
|
||||
|
||||
part 'discount_remove_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DiscountRemoveResponseDTO extends BasicResponseDTO {
|
||||
DiscountRemoveResponseDTO(
|
||||
{
|
||||
required this.receipt,
|
||||
required super.succeeded,
|
||||
required super.message});
|
||||
|
||||
PriceInformation receipt;
|
||||
|
||||
factory DiscountRemoveResponseDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$DiscountRemoveResponseDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$DiscountRemoveResponseDTOToJson(this);
|
||||
}
|
||||
23
lib/dto/discount_remove_response.g.dart
Normal file
23
lib/dto/discount_remove_response.g.dart
Normal file
@ -0,0 +1,23 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'discount_remove_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DiscountRemoveResponseDTO _$DiscountRemoveResponseDTOFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => DiscountRemoveResponseDTO(
|
||||
receipt: PriceInformation.fromJson(json['receipt'] as Map<String, dynamic>),
|
||||
succeeded: json['succeeded'] as bool,
|
||||
message: json['message'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DiscountRemoveResponseDTOToJson(
|
||||
DiscountRemoveResponseDTO instance,
|
||||
) => <String, dynamic>{
|
||||
'succeeded': instance.succeeded,
|
||||
'message': instance.message,
|
||||
'receipt': instance.receipt,
|
||||
};
|
||||
18
lib/dto/discount_update.dart
Normal file
18
lib/dto/discount_update.dart
Normal file
@ -0,0 +1,18 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'discount_update.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DiscountUpdateDTO {
|
||||
DiscountUpdateDTO(
|
||||
{required this.note, required this.deliveryId, required this.discount});
|
||||
|
||||
String? note;
|
||||
String deliveryId;
|
||||
int? discount;
|
||||
|
||||
factory DiscountUpdateDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$DiscountUpdateDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$DiscountUpdateDTOToJson(this);
|
||||
}
|
||||
21
lib/dto/discount_update.g.dart
Normal file
21
lib/dto/discount_update.g.dart
Normal file
@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'discount_update.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DiscountUpdateDTO _$DiscountUpdateDTOFromJson(Map<String, dynamic> json) =>
|
||||
DiscountUpdateDTO(
|
||||
note: json['note'] as String?,
|
||||
deliveryId: json['delivery_id'] as String,
|
||||
discount: (json['discount'] as num?)?.toInt(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DiscountUpdateDTOToJson(DiscountUpdateDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'note': instance.note,
|
||||
'delivery_id': instance.deliveryId,
|
||||
'discount': instance.discount,
|
||||
};
|
||||
22
lib/dto/discount_update_response.dart
Normal file
22
lib/dto/discount_update_response.dart
Normal file
@ -0,0 +1,22 @@
|
||||
import 'package:hl_lieferservice/dto/article.dart';
|
||||
|
||||
import 'basic_response.dart';
|
||||
import 'discount_add_response.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'discount_update_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DiscountUpdateResponseDTO extends BasicResponseDTO {
|
||||
DiscountUpdateResponseDTO(
|
||||
{required this.values,
|
||||
required super.succeeded,
|
||||
required super.message});
|
||||
|
||||
UpdatedValues? values;
|
||||
|
||||
factory DiscountUpdateResponseDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$DiscountUpdateResponseDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$DiscountUpdateResponseDTOToJson(this);
|
||||
}
|
||||
26
lib/dto/discount_update_response.g.dart
Normal file
26
lib/dto/discount_update_response.g.dart
Normal file
@ -0,0 +1,26 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'discount_update_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DiscountUpdateResponseDTO _$DiscountUpdateResponseDTOFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => DiscountUpdateResponseDTO(
|
||||
values:
|
||||
json['values'] == null
|
||||
? null
|
||||
: UpdatedValues.fromJson(json['values'] as Map<String, dynamic>),
|
||||
succeeded: json['succeeded'] as bool,
|
||||
message: json['message'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DiscountUpdateResponseDTOToJson(
|
||||
DiscountUpdateResponseDTO instance,
|
||||
) => <String, dynamic>{
|
||||
'succeeded': instance.succeeded,
|
||||
'message': instance.message,
|
||||
'values': instance.values,
|
||||
};
|
||||
16
lib/dto/driver.dart
Normal file
16
lib/dto/driver.dart
Normal file
@ -0,0 +1,16 @@
|
||||
import 'car.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'driver.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class DriverDTO {
|
||||
DriverDTO({required this.id, required this.name, required this.salutation, required this.cars});
|
||||
String id;
|
||||
String name;
|
||||
String salutation;
|
||||
List<CarDTO> cars;
|
||||
|
||||
factory DriverDTO.fromJson(Map<String, dynamic> json) => _$DriverDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$DriverDTOToJson(this);
|
||||
}
|
||||
24
lib/dto/driver.g.dart
Normal file
24
lib/dto/driver.g.dart
Normal file
@ -0,0 +1,24 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'driver.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
DriverDTO _$DriverDTOFromJson(Map<String, dynamic> json) => DriverDTO(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
salutation: json['salutation'] as String,
|
||||
cars:
|
||||
(json['cars'] as List<dynamic>)
|
||||
.map((e) => CarDTO.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$DriverDTOToJson(DriverDTO instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'name': instance.name,
|
||||
'salutation': instance.salutation,
|
||||
'cars': instance.cars,
|
||||
};
|
||||
18
lib/dto/image.dart
Normal file
18
lib/dto/image.dart
Normal file
@ -0,0 +1,18 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'image.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class ImageDTO {
|
||||
ImageDTO(
|
||||
{required this.url, required this.name, required this.oid});
|
||||
|
||||
String url;
|
||||
String name;
|
||||
String oid;
|
||||
|
||||
factory ImageDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$ImageDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$ImageDTOToJson(this);
|
||||
}
|
||||
19
lib/dto/image.g.dart
Normal file
19
lib/dto/image.g.dart
Normal file
@ -0,0 +1,19 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'image.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ImageDTO _$ImageDTOFromJson(Map<String, dynamic> json) => ImageDTO(
|
||||
url: json['url'] as String,
|
||||
name: json['name'] as String,
|
||||
oid: json['oid'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ImageDTOToJson(ImageDTO instance) => <String, dynamic>{
|
||||
'url': instance.url,
|
||||
'name': instance.name,
|
||||
'oid': instance.oid,
|
||||
};
|
||||
16
lib/dto/note.dart
Normal file
16
lib/dto/note.dart
Normal file
@ -0,0 +1,16 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'note.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class NoteDTO {
|
||||
NoteDTO(
|
||||
{required this.id,
|
||||
required this.note});
|
||||
|
||||
final String id;
|
||||
final String note;
|
||||
|
||||
factory NoteDTO.fromJson(Map<String, dynamic> json) => _$NoteDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$NoteDTOToJson(this);
|
||||
}
|
||||
15
lib/dto/note.g.dart
Normal file
15
lib/dto/note.g.dart
Normal file
@ -0,0 +1,15 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'note.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
NoteDTO _$NoteDTOFromJson(Map<String, dynamic> json) =>
|
||||
NoteDTO(id: json['id'] as String, note: json['note'] as String);
|
||||
|
||||
Map<String, dynamic> _$NoteDTOToJson(NoteDTO instance) => <String, dynamic>{
|
||||
'id': instance.id,
|
||||
'note': instance.note,
|
||||
};
|
||||
16
lib/dto/note_add_response.dart
Normal file
16
lib/dto/note_add_response.dart
Normal file
@ -0,0 +1,16 @@
|
||||
import 'package:hl_lieferservice/dto/basic_response.dart';
|
||||
import 'package:hl_lieferservice/dto/note.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'note_add_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class NoteAddResponseDTO extends BasicResponseDTO {
|
||||
NoteAddResponseDTO(
|
||||
{required this.note, required super.succeeded, required super.message});
|
||||
|
||||
final NoteDTO? note;
|
||||
|
||||
factory NoteAddResponseDTO.fromJson(Map<String, dynamic> json) => _$NoteAddResponseDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$NoteAddResponseDTOToJson(this);
|
||||
}
|
||||
24
lib/dto/note_add_response.g.dart
Normal file
24
lib/dto/note_add_response.g.dart
Normal file
@ -0,0 +1,24 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'note_add_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
NoteAddResponseDTO _$NoteAddResponseDTOFromJson(Map<String, dynamic> json) =>
|
||||
NoteAddResponseDTO(
|
||||
note:
|
||||
json['note'] == null
|
||||
? null
|
||||
: NoteDTO.fromJson(json['note'] as Map<String, dynamic>),
|
||||
succeeded: json['succeeded'] as bool,
|
||||
message: json['message'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NoteAddResponseDTOToJson(NoteAddResponseDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'succeeded': instance.succeeded,
|
||||
'message': instance.message,
|
||||
'note': instance.note,
|
||||
};
|
||||
19
lib/dto/note_get_response.dart
Normal file
19
lib/dto/note_get_response.dart
Normal file
@ -0,0 +1,19 @@
|
||||
import 'note.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'note_get_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class NoteGetResponseDTO {
|
||||
NoteGetResponseDTO(
|
||||
{required this.notes, required this.succeeded, required this.message});
|
||||
|
||||
final List<NoteDTO> notes;
|
||||
final bool succeeded;
|
||||
final String message;
|
||||
|
||||
factory NoteGetResponseDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$NoteGetResponseDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$NoteGetResponseDTOToJson(this);
|
||||
}
|
||||
24
lib/dto/note_get_response.g.dart
Normal file
24
lib/dto/note_get_response.g.dart
Normal file
@ -0,0 +1,24 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'note_get_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
NoteGetResponseDTO _$NoteGetResponseDTOFromJson(Map<String, dynamic> json) =>
|
||||
NoteGetResponseDTO(
|
||||
notes:
|
||||
(json['notes'] as List<dynamic>)
|
||||
.map((e) => NoteDTO.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
succeeded: json['succeeded'] as bool,
|
||||
message: json['message'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NoteGetResponseDTOToJson(NoteGetResponseDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'notes': instance.notes,
|
||||
'succeeded': instance.succeeded,
|
||||
'message': instance.message,
|
||||
};
|
||||
18
lib/dto/note_template.dart
Normal file
18
lib/dto/note_template.dart
Normal file
@ -0,0 +1,18 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'note_template.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class NoteTemplateDTO {
|
||||
NoteTemplateDTO(
|
||||
{required this.language,
|
||||
required this.title,
|
||||
required this.note});
|
||||
|
||||
final String note;
|
||||
final String language;
|
||||
final String title;
|
||||
|
||||
factory NoteTemplateDTO.fromJson(Map<String, dynamic> json) => _$NoteTemplateDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$NoteTemplateDTOToJson(this);
|
||||
}
|
||||
21
lib/dto/note_template.g.dart
Normal file
21
lib/dto/note_template.g.dart
Normal file
@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'note_template.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
NoteTemplateDTO _$NoteTemplateDTOFromJson(Map<String, dynamic> json) =>
|
||||
NoteTemplateDTO(
|
||||
language: json['language'] as String,
|
||||
title: json['title'] as String,
|
||||
note: json['note'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NoteTemplateDTOToJson(NoteTemplateDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'note': instance.note,
|
||||
'language': instance.language,
|
||||
'title': instance.title,
|
||||
};
|
||||
19
lib/dto/note_template_response.dart
Normal file
19
lib/dto/note_template_response.dart
Normal file
@ -0,0 +1,19 @@
|
||||
import 'note_template.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'note_template_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class NoteTemplateResponseDTO {
|
||||
NoteTemplateResponseDTO(
|
||||
{required this.notes, required this.succeeded, required this.message});
|
||||
|
||||
final List<NoteTemplateDTO> notes;
|
||||
final bool succeeded;
|
||||
final String message;
|
||||
|
||||
factory NoteTemplateResponseDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$NoteTemplateResponseDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$NoteTemplateResponseDTOToJson(this);
|
||||
}
|
||||
26
lib/dto/note_template_response.g.dart
Normal file
26
lib/dto/note_template_response.g.dart
Normal file
@ -0,0 +1,26 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'note_template_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
NoteTemplateResponseDTO _$NoteTemplateResponseDTOFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => NoteTemplateResponseDTO(
|
||||
notes:
|
||||
(json['notes'] as List<dynamic>)
|
||||
.map((e) => NoteTemplateDTO.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
succeeded: json['succeeded'] as bool,
|
||||
message: json['message'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$NoteTemplateResponseDTOToJson(
|
||||
NoteTemplateResponseDTO instance,
|
||||
) => <String, dynamic>{
|
||||
'notes': instance.notes,
|
||||
'succeeded': instance.succeeded,
|
||||
'message': instance.message,
|
||||
};
|
||||
17
lib/dto/payment.dart
Normal file
17
lib/dto/payment.dart
Normal file
@ -0,0 +1,17 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'payment.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class PaymentMethodDTO {
|
||||
PaymentMethodDTO(this.id,
|
||||
{required this.description,
|
||||
required this.shortCode});
|
||||
|
||||
final String id;
|
||||
final String description;
|
||||
final String shortCode;
|
||||
|
||||
factory PaymentMethodDTO.fromJson(Map<String, dynamic> json) => _$PaymentMethodDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$PaymentMethodDTOToJson(this);
|
||||
}
|
||||
21
lib/dto/payment.g.dart
Normal file
21
lib/dto/payment.g.dart
Normal file
@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'payment.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
PaymentMethodDTO _$PaymentMethodDTOFromJson(Map<String, dynamic> json) =>
|
||||
PaymentMethodDTO(
|
||||
json['id'] as String,
|
||||
description: json['description'] as String,
|
||||
shortCode: json['shortCode'] as String,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PaymentMethodDTOToJson(PaymentMethodDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'description': instance.description,
|
||||
'shortCode': instance.shortCode,
|
||||
};
|
||||
14
lib/dto/payments.dart
Normal file
14
lib/dto/payments.dart
Normal file
@ -0,0 +1,14 @@
|
||||
import 'payment.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'payments.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class PaymentMethodListDTO {
|
||||
PaymentMethodListDTO({required this.paymentMethods});
|
||||
|
||||
final List<PaymentMethodDTO> paymentMethods;
|
||||
|
||||
factory PaymentMethodListDTO.fromJson(Map<String, dynamic> json) => _$PaymentMethodListDTOFromJson(json);
|
||||
Map<dynamic, dynamic> toJson() => _$PaymentMethodListDTOToJson(this);
|
||||
}
|
||||
20
lib/dto/payments.g.dart
Normal file
20
lib/dto/payments.g.dart
Normal file
@ -0,0 +1,20 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'payments.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
PaymentMethodListDTO _$PaymentMethodListDTOFromJson(
|
||||
Map<String, dynamic> json,
|
||||
) => PaymentMethodListDTO(
|
||||
paymentMethods:
|
||||
(json['paymentMethods'] as List<dynamic>)
|
||||
.map((e) => PaymentMethodDTO.fromJson(e as Map<String, dynamic>))
|
||||
.toList(),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$PaymentMethodListDTOToJson(
|
||||
PaymentMethodListDTO instance,
|
||||
) => <String, dynamic>{'paymentMethods': instance.paymentMethods};
|
||||
15
lib/dto/scan.dart
Normal file
15
lib/dto/scan.dart
Normal file
@ -0,0 +1,15 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'scan.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class ScanDTO {
|
||||
ScanDTO({required this.internalId});
|
||||
|
||||
String internalId;
|
||||
|
||||
factory ScanDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$ScanDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$ScanDTOToJson(this);
|
||||
}
|
||||
14
lib/dto/scan.g.dart
Normal file
14
lib/dto/scan.g.dart
Normal file
@ -0,0 +1,14 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'scan.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ScanDTO _$ScanDTOFromJson(Map<String, dynamic> json) =>
|
||||
ScanDTO(internalId: json['internal_id'] as String);
|
||||
|
||||
Map<String, dynamic> _$ScanDTOToJson(ScanDTO instance) => <String, dynamic>{
|
||||
'internal_id': instance.internalId,
|
||||
};
|
||||
18
lib/dto/scan_response.dart
Normal file
18
lib/dto/scan_response.dart
Normal file
@ -0,0 +1,18 @@
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'scan_response.g.dart';
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.snake)
|
||||
class ScanResponseDTO {
|
||||
ScanResponseDTO(
|
||||
{required this.message, required this.succeeded, required this.noteId});
|
||||
|
||||
final bool succeeded;
|
||||
final String message;
|
||||
final String? noteId;
|
||||
|
||||
factory ScanResponseDTO.fromJson(Map<String, dynamic> json) =>
|
||||
_$ScanResponseDTOFromJson(json);
|
||||
|
||||
Map<dynamic, dynamic> toJson() => _$ScanResponseDTOToJson(this);
|
||||
}
|
||||
21
lib/dto/scan_response.g.dart
Normal file
21
lib/dto/scan_response.g.dart
Normal file
@ -0,0 +1,21 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'scan_response.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
ScanResponseDTO _$ScanResponseDTOFromJson(Map<String, dynamic> json) =>
|
||||
ScanResponseDTO(
|
||||
message: json['message'] as String,
|
||||
succeeded: json['succeeded'] as bool,
|
||||
noteId: json['note_id'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$ScanResponseDTOToJson(ScanResponseDTO instance) =>
|
||||
<String, dynamic>{
|
||||
'succeeded': instance.succeeded,
|
||||
'message': instance.message,
|
||||
'note_id': instance.noteId,
|
||||
};
|
||||
28
lib/feature/authentication/bloc/auth_bloc.dart
Normal file
28
lib/feature/authentication/bloc/auth_bloc.dart
Normal file
@ -0,0 +1,28 @@
|
||||
import 'package:hl_lieferservice/feature/authentication/bloc/auth_event.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/bloc/auth_state.dart';
|
||||
import 'package:hl_lieferservice/repository/user_repository.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_event.dart';
|
||||
|
||||
class AuthBloc extends Bloc<AuthEvent, AuthState> {
|
||||
UserRepository repository;
|
||||
OperationBloc operationBloc;
|
||||
|
||||
AuthBloc({required this.repository, required this.operationBloc})
|
||||
: super(Unauthenticated()) {
|
||||
on<Authenticate>(_auth);
|
||||
on<Logout>(_logout);
|
||||
}
|
||||
|
||||
Future<void> _auth(Authenticate event, Emitter<AuthState> emit) async {
|
||||
operationBloc.add(LoadOperation());
|
||||
await Future.delayed(Duration(seconds: 5));
|
||||
emit(Authenticated(teamId: event.username));
|
||||
operationBloc.add(FinishOperation());
|
||||
}
|
||||
|
||||
Future<void> _logout(Logout event, Emitter<AuthState> emit) async {
|
||||
emit(Unauthenticated());
|
||||
}
|
||||
}
|
||||
14
lib/feature/authentication/bloc/auth_event.dart
Normal file
14
lib/feature/authentication/bloc/auth_event.dart
Normal file
@ -0,0 +1,14 @@
|
||||
abstract class AuthEvent {}
|
||||
|
||||
class Authenticate extends AuthEvent {
|
||||
String username;
|
||||
String password;
|
||||
|
||||
Authenticate({required this.username, required this.password});
|
||||
}
|
||||
|
||||
class Logout extends AuthEvent {
|
||||
String username;
|
||||
|
||||
Logout({required this.username});
|
||||
}
|
||||
9
lib/feature/authentication/bloc/auth_state.dart
Normal file
9
lib/feature/authentication/bloc/auth_state.dart
Normal file
@ -0,0 +1,9 @@
|
||||
abstract class AuthState {}
|
||||
|
||||
class Unauthenticated extends AuthState {}
|
||||
class Authenticated extends AuthState {
|
||||
String teamId;
|
||||
|
||||
Authenticated({required this.teamId});
|
||||
}
|
||||
|
||||
25
lib/feature/authentication/presentation/login_enforcer.dart
Normal file
25
lib/feature/authentication/presentation/login_enforcer.dart
Normal file
@ -0,0 +1,25 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/presentation/login_page.dart';
|
||||
|
||||
import '../bloc/auth_bloc.dart';
|
||||
import '../bloc/auth_state.dart';
|
||||
|
||||
class LoginEnforcer extends StatelessWidget {
|
||||
final Widget child;
|
||||
|
||||
const LoginEnforcer({super.key, required this.child});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocBuilder<AuthBloc, AuthState>(
|
||||
builder: (context, state) {
|
||||
if (state is Authenticated) {
|
||||
return child;
|
||||
}
|
||||
|
||||
return LoginPage();
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
121
lib/feature/authentication/presentation/login_page.dart
Normal file
121
lib/feature/authentication/presentation/login_page.dart
Normal file
@ -0,0 +1,121 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/bloc/auth_event.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_event.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/presentation/operation_view_enforcer.dart';
|
||||
|
||||
import '../bloc/auth_bloc.dart';
|
||||
|
||||
class LoginPage extends StatefulWidget {
|
||||
const LoginPage({super.key});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _LoginPageState();
|
||||
}
|
||||
|
||||
class _LoginPageState extends State<LoginPage> {
|
||||
final _loginFormKey = GlobalKey<FormState>();
|
||||
final TextEditingController _passwordEditingController =
|
||||
TextEditingController();
|
||||
final TextEditingController _userIdEditingController =
|
||||
TextEditingController();
|
||||
|
||||
bool _isEmpty = false;
|
||||
|
||||
void onChanged(String value) {
|
||||
setState(() {
|
||||
_isEmpty = value.isEmpty;
|
||||
});
|
||||
}
|
||||
|
||||
void _onPressLogin(BuildContext context) async {
|
||||
if (context.mounted) {
|
||||
context.read<AuthBloc>().add(
|
||||
Authenticate(
|
||||
username: _userIdEditingController.text,
|
||||
password: _passwordEditingController.text,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(50),
|
||||
child: Column(
|
||||
children: [
|
||||
Image.asset(
|
||||
"assets/holzleitner_Logo_2017_RZ_transparent.png",
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 20),
|
||||
child: Text(
|
||||
"Auslieferservice",
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w400,
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Form(
|
||||
key: _loginFormKey,
|
||||
child: FractionallySizedBox(
|
||||
widthFactor: 0.8,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Personalnummer",
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(10.0),
|
||||
),
|
||||
),
|
||||
),
|
||||
controller: _userIdEditingController,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
),
|
||||
TextFormField(
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Passwort",
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(10.0)),
|
||||
),
|
||||
),
|
||||
controller: _passwordEditingController,
|
||||
obscureText: true,
|
||||
onChanged: onChanged,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 15, bottom: 15),
|
||||
child: OutlinedButton(
|
||||
onPressed:
|
||||
!_isEmpty ? () => _onPressLogin(context) : null,
|
||||
child: const Text("Anmelden"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
106
lib/feature/cars/bloc/cars_bloc.dart
Normal file
106
lib/feature/cars/bloc/cars_bloc.dart
Normal file
@ -0,0 +1,106 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/cars/repository/cars_repository.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_event.dart';
|
||||
|
||||
import '../../../model/car.dart';
|
||||
import 'cars_event.dart';
|
||||
import 'cars_state.dart';
|
||||
|
||||
class CarsBloc extends Bloc<CarEvents, CarsState> {
|
||||
CarsRepository repository;
|
||||
OperationBloc opBloc;
|
||||
|
||||
CarsBloc({required this.repository, required this.opBloc})
|
||||
: super(CarsInitial()) {
|
||||
on<CarAdd>(_carAdd);
|
||||
on<CarEdit>(_carEdit);
|
||||
on<CarDelete>(_carDelete);
|
||||
on<CarLoad>(_carLoad);
|
||||
}
|
||||
|
||||
Future<void> _carLoad(CarLoad event, Emitter<CarsState> emit) async {
|
||||
try {
|
||||
emit(CarsLoading());
|
||||
List<Car> cars = await repository.getAll(event.teamId);
|
||||
emit(CarsLoaded(cars: cars, teamId: event.teamId));
|
||||
} catch (e) {
|
||||
emit(CarsLoadingFailed());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _carAdd(CarAdd event, Emitter<CarsState> emit) async {
|
||||
final currentState = state;
|
||||
|
||||
try {
|
||||
opBloc.add(LoadOperation());
|
||||
Car newCar = await repository.add(event.teamId, event.plate);
|
||||
|
||||
if (currentState is CarsLoaded) {
|
||||
emit(
|
||||
currentState.copyWith(
|
||||
cars: List<Car>.from(currentState.cars)..add(newCar),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
opBloc.add(FinishOperation(message: "Auto erfolgreich hinzugefügt"));
|
||||
} catch (e) {
|
||||
opBloc.add(FailOperation(message: "Fehler beim Hinzufügen eines Autos"));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _carEdit(CarEdit event, Emitter<CarsState> emit) async {
|
||||
final currentState = state;
|
||||
|
||||
try {
|
||||
opBloc.add(LoadOperation());
|
||||
await repository.edit(event.teamId, event.newCar);
|
||||
|
||||
if (currentState is CarsLoaded) {
|
||||
emit(
|
||||
currentState.copyWith(
|
||||
cars:
|
||||
List<Car>.from(currentState.cars).map((car) {
|
||||
if (car.id == event.newCar.id) {
|
||||
return event.newCar;
|
||||
}
|
||||
|
||||
return car;
|
||||
}).toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
opBloc.add(FinishOperation(message: "Auto erfolgreich editiert"));
|
||||
} catch (e) {
|
||||
opBloc.add(FailOperation(message: "Fehler beim Editieren des Autos"));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _carDelete(CarDelete event, Emitter<CarsState> emit) async {
|
||||
final currentState = state;
|
||||
|
||||
try {
|
||||
opBloc.add(LoadOperation());
|
||||
await repository.delete(event.carId, event.teamId);
|
||||
|
||||
if (currentState is CarsLoaded) {
|
||||
emit(
|
||||
CarsLoaded(
|
||||
cars: [
|
||||
...currentState.cars.where(
|
||||
(car) => car.id != int.parse(event.carId),
|
||||
),
|
||||
],
|
||||
teamId: currentState.teamId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
opBloc.add(FinishOperation(message: "Auto erfolgreich gelöscht"));
|
||||
} catch (e) {
|
||||
opBloc.add(FailOperation(message: "Fehler beim Löschen des Autos"));
|
||||
}
|
||||
}
|
||||
}
|
||||
30
lib/feature/cars/bloc/cars_event.dart
Normal file
30
lib/feature/cars/bloc/cars_event.dart
Normal file
@ -0,0 +1,30 @@
|
||||
import '../../../model/car.dart';
|
||||
|
||||
abstract class CarEvents {}
|
||||
|
||||
class CarLoad extends CarEvents {
|
||||
String teamId;
|
||||
|
||||
CarLoad({required this.teamId});
|
||||
}
|
||||
|
||||
class CarEdit extends CarEvents {
|
||||
Car newCar;
|
||||
String teamId;
|
||||
|
||||
CarEdit({required this.newCar, required this.teamId});
|
||||
}
|
||||
|
||||
class CarAdd extends CarEvents {
|
||||
String teamId;
|
||||
String plate;
|
||||
|
||||
CarAdd({required this.teamId, required this.plate});
|
||||
}
|
||||
|
||||
class CarDelete extends CarEvents {
|
||||
String carId;
|
||||
String teamId;
|
||||
|
||||
CarDelete({required this.carId, required this.teamId});
|
||||
}
|
||||
23
lib/feature/cars/bloc/cars_state.dart
Normal file
23
lib/feature/cars/bloc/cars_state.dart
Normal file
@ -0,0 +1,23 @@
|
||||
import 'package:hl_lieferservice/model/car.dart';
|
||||
|
||||
abstract class CarsState {}
|
||||
|
||||
class CarsInitial extends CarsState {}
|
||||
|
||||
class CarsLoading extends CarsState {}
|
||||
|
||||
class CarsLoadingFailed extends CarsState {}
|
||||
|
||||
class CarsLoaded extends CarsState {
|
||||
List<Car> cars;
|
||||
String teamId;
|
||||
|
||||
CarsLoaded({required this.cars, required this.teamId});
|
||||
|
||||
CarsLoaded copyWith({List<Car>? cars, String? teamId}) {
|
||||
return CarsLoaded(
|
||||
cars: cars ?? this.cars,
|
||||
teamId: teamId ?? this.teamId,
|
||||
);
|
||||
}
|
||||
}
|
||||
75
lib/feature/cars/presentation/car_card.dart
Normal file
75
lib/feature/cars/presentation/car_card.dart
Normal file
@ -0,0 +1,75 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../model/car.dart';
|
||||
import 'car_dialog.dart';
|
||||
|
||||
class CarCard extends StatelessWidget {
|
||||
final Car car;
|
||||
final Function(Car car) onDelete;
|
||||
final Function(Car car, String newName) onEdit;
|
||||
|
||||
const CarCard({
|
||||
super.key,
|
||||
required this.car,
|
||||
required this.onEdit,
|
||||
required this.onDelete,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: Icon(
|
||||
Icons.local_shipping,
|
||||
size: 32,
|
||||
color: Theme.of(context).primaryColor,
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: Text(car.plate),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Row(
|
||||
children: [
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return CarDialog(
|
||||
onAction: (plate) {
|
||||
onEdit(car, plate);
|
||||
},
|
||||
action: CarAction.edit,
|
||||
initialPlateValue: car.plate,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: Icon(Icons.edit, color: Theme.of(context).primaryColor),
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
onDelete(car);
|
||||
},
|
||||
icon: const Icon(Icons.delete, color: Colors.redAccent),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
92
lib/feature/cars/presentation/car_dialog.dart
Normal file
92
lib/feature/cars/presentation/car_dialog.dart
Normal file
@ -0,0 +1,92 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum CarAction { edit, add }
|
||||
|
||||
class CarDialog extends StatefulWidget {
|
||||
const CarDialog({
|
||||
super.key,
|
||||
required this.onAction,
|
||||
this.action = CarAction.add,
|
||||
this.initialPlateValue = "",
|
||||
});
|
||||
|
||||
final CarAction action;
|
||||
final String initialPlateValue;
|
||||
final Function(String) onAction;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _CarDialogState();
|
||||
}
|
||||
|
||||
class _CarDialogState extends State<CarDialog> {
|
||||
bool _isLoading = false;
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late final TextEditingController _plateController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_plateController = TextEditingController.fromValue(
|
||||
TextEditingValue(text: widget.initialPlateValue),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
content: SizedBox(
|
||||
height: 120,
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10.0),
|
||||
child: Text(
|
||||
"Fahrzeug ${widget.action == CarAction.add ? "hinzufügen" : "bearbeiten"}",
|
||||
style: const TextStyle(
|
||||
fontSize: 18.0,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
TextFormField(
|
||||
validator: (value) {
|
||||
if (value == "") {
|
||||
return "Kennzeichen darf nicht leer sein";
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
decoration: const InputDecoration(labelText: "Kennzeichen"),
|
||||
controller: _plateController,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
actions: [
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text("Abbrechen"),
|
||||
),
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
widget.onAction(_plateController.text);
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
widget.action == CarAction.add ? "Hinzufügen" : "Bearbeiten",
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
78
lib/feature/cars/presentation/car_management.dart
Normal file
78
lib/feature/cars/presentation/car_management.dart
Normal file
@ -0,0 +1,78 @@
|
||||
import 'package:hl_lieferservice/feature/cars/presentation/car_card.dart';
|
||||
|
||||
import '../../../model/car.dart';
|
||||
import 'car_dialog.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CarManagementOverview extends StatefulWidget {
|
||||
final List<Car> cars;
|
||||
final Function(String plate) onAdd;
|
||||
final Function(String id) onDelete;
|
||||
final Function(String id, String plate) onEdit;
|
||||
|
||||
const CarManagementOverview({
|
||||
super.key,
|
||||
required this.cars,
|
||||
required this.onDelete,
|
||||
required this.onEdit,
|
||||
required this.onAdd,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _CarManagementOverviewState();
|
||||
}
|
||||
|
||||
class _CarManagementOverviewState extends State<CarManagementOverview> {
|
||||
void _addCar() async {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return CarDialog(onAction: widget.onAdd);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _removeCar(Car car) async {
|
||||
widget.onDelete(car.id.toString());
|
||||
}
|
||||
|
||||
void _editCar(Car car, String newName) async {
|
||||
widget.onEdit(car.id.toString(), newName);
|
||||
}
|
||||
|
||||
Widget _buildCarOverview() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: widget.cars.isEmpty ? const Center(child: Text("keine Fahrzeuge vorhanden")) : ListView.builder(
|
||||
itemBuilder:
|
||||
(context, index) => CarCard(
|
||||
car: widget.cars[index],
|
||||
onEdit: _editCar,
|
||||
onDelete: _removeCar,
|
||||
),
|
||||
itemCount: widget.cars.length,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(
|
||||
"Fahrzeugverwaltung",
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _addCar,
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
child: Icon(
|
||||
Icons.add,
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
),
|
||||
body: _buildCarOverview(),
|
||||
);
|
||||
}
|
||||
}
|
||||
82
lib/feature/cars/presentation/car_management_page.dart
Normal file
82
lib/feature/cars/presentation/car_management_page.dart
Normal file
@ -0,0 +1,82 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/bloc/auth_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/authentication/bloc/auth_state.dart';
|
||||
import 'package:hl_lieferservice/feature/cars/bloc/cars_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/cars/bloc/cars_event.dart';
|
||||
import 'package:hl_lieferservice/feature/cars/bloc/cars_state.dart';
|
||||
import 'package:hl_lieferservice/feature/cars/presentation/car_management.dart';
|
||||
import 'package:hl_lieferservice/model/car.dart';
|
||||
|
||||
class CarManagementPage extends StatefulWidget {
|
||||
const CarManagementPage({super.key});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _CarManagementPageState();
|
||||
}
|
||||
|
||||
class _CarManagementPageState extends State<CarManagementPage> {
|
||||
late Authenticated _authState;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// Load cars
|
||||
_authState = context.read<AuthBloc>().state as Authenticated;
|
||||
context.read<CarsBloc>().add(CarLoad(teamId: _authState.teamId));
|
||||
}
|
||||
|
||||
void _add(String plate) {
|
||||
context.read<CarsBloc>().add(
|
||||
CarAdd(teamId: _authState.teamId, plate: plate),
|
||||
);
|
||||
}
|
||||
|
||||
void _remove(String id) {
|
||||
context.read<CarsBloc>().add(
|
||||
CarDelete(carId: id, teamId: _authState.teamId),
|
||||
);
|
||||
}
|
||||
|
||||
void _edit(String id, String plate) {
|
||||
context.read<CarsBloc>().add(
|
||||
CarEdit(
|
||||
newCar: Car(id: int.parse(id), plate: plate),
|
||||
teamId: _authState.teamId,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: BlocBuilder<CarsBloc, CarsState>(
|
||||
builder: (context, state) {
|
||||
debugPrint('BlocBuilder rebuilding with state: $state');
|
||||
|
||||
if (state is CarsLoading) {
|
||||
return Center(child: const CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (state is CarsLoaded) {
|
||||
return CarManagementOverview(
|
||||
cars: state.cars,
|
||||
onEdit: _edit,
|
||||
onAdd: _add,
|
||||
onDelete: _remove,
|
||||
);
|
||||
}
|
||||
|
||||
if (state is CarsLoadingFailed) {
|
||||
return Center(
|
||||
child: const Text("Fahrzeuge konnten nicht geladen werden"),
|
||||
);
|
||||
}
|
||||
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
25
lib/feature/cars/repository/cars_repository.dart
Normal file
25
lib/feature/cars/repository/cars_repository.dart
Normal file
@ -0,0 +1,25 @@
|
||||
import 'package:hl_lieferservice/feature/cars/service/cars_service.dart';
|
||||
|
||||
import '../../../model/car.dart';
|
||||
|
||||
class CarsRepository {
|
||||
CarService service;
|
||||
|
||||
CarsRepository({required this.service});
|
||||
|
||||
Future<List<Car>> getAll(String teamId) async {
|
||||
return service.getCars(int.parse(teamId));
|
||||
}
|
||||
|
||||
Future<void> delete(String carId, String teamId) async {
|
||||
return service.removeCar(int.parse(carId), int.parse(teamId));
|
||||
}
|
||||
|
||||
Future<void> edit(String teamId, Car newCar) async {
|
||||
return service.editCar(newCar);
|
||||
}
|
||||
|
||||
Future<Car> add(String teamId, String plate) async {
|
||||
return service.addCar(plate, int.parse(teamId));
|
||||
}
|
||||
}
|
||||
146
lib/feature/cars/service/cars_service.dart
Normal file
146
lib/feature/cars/service/cars_service.dart
Normal file
@ -0,0 +1,146 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:docuframe/docuframe.dart' as df;
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:hl_lieferservice/services/erpframe.dart';
|
||||
|
||||
import '../../../dto/basic_response.dart';
|
||||
import '../../../dto/car_add.dart';
|
||||
import '../../../dto/car_add_response.dart';
|
||||
import '../../../dto/car_get_response.dart';
|
||||
import '../../../model/car.dart';
|
||||
|
||||
class CarService extends ErpFrameService {
|
||||
CarService({required super.config});
|
||||
|
||||
Future<Car> addCar(String plate, int teamId) async {
|
||||
df.LoginSession? session;
|
||||
|
||||
try {
|
||||
session = await getSession();
|
||||
df.DocuFrameMacroResponse response =
|
||||
await df.Macro(config: dfConfig, session: session).execute(
|
||||
"_web_addCar",
|
||||
parameter: CarAddDTO.make(teamId, plate).toJson()
|
||||
as Map<String, dynamic>);
|
||||
|
||||
Map<String, dynamic> responseJson = jsonDecode(response.body!);
|
||||
debugPrint(responseJson.toString());
|
||||
CarAddResponseDTO responseDto = CarAddResponseDTO.fromJson(responseJson);
|
||||
|
||||
if (responseDto.succeeded == true) {
|
||||
return Car(
|
||||
id: int.parse(responseDto.car.id), plate: responseDto.car.plate);
|
||||
} else {
|
||||
throw responseDto.message;
|
||||
}
|
||||
} catch (e, st) {
|
||||
debugPrint("ERROR WHILE ADDING CAR");
|
||||
debugPrint(e.toString());
|
||||
debugPrint(st.toString());
|
||||
|
||||
rethrow;
|
||||
} finally {
|
||||
await logout(session);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> editCar(Car car) async {
|
||||
df.LoginSession? session;
|
||||
|
||||
try {
|
||||
session = await getSession();
|
||||
|
||||
debugPrint(car.plate);
|
||||
debugPrint(car.id.toString());
|
||||
|
||||
df.DocuFrameMacroResponse response =
|
||||
await df.Macro(config: dfConfig, session: session).execute(
|
||||
"_web_editCar",
|
||||
parameter: {"id": car.id, "plate": car.plate});
|
||||
|
||||
Map<String, dynamic> responseJson = jsonDecode(response.body!);
|
||||
debugPrint(responseJson.toString());
|
||||
|
||||
BasicResponseDTO responseDto = BasicResponseDTO.fromJson(responseJson);
|
||||
|
||||
if (responseDto.succeeded == true) {
|
||||
return;
|
||||
} else {
|
||||
throw responseDto.message;
|
||||
}
|
||||
} catch (e, st) {
|
||||
debugPrint("ERROR WHILE EDITING CAR ${car.id}");
|
||||
debugPrint("$e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
rethrow;
|
||||
} finally {
|
||||
await logout(session);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> removeCar(int carId, int teamId) async {
|
||||
df.LoginSession? session;
|
||||
|
||||
try {
|
||||
session = await getSession();
|
||||
df.DocuFrameMacroResponse response =
|
||||
await df.Macro(config: dfConfig, session: session).execute(
|
||||
"_web_removeCar",
|
||||
parameter: {"team_id": teamId, "id": carId});
|
||||
|
||||
Map<String, dynamic> responseJson = jsonDecode(response.body!);
|
||||
debugPrint(responseJson.toString());
|
||||
BasicResponseDTO responseDto = BasicResponseDTO.fromJson(responseJson);
|
||||
|
||||
if (responseDto.succeeded == true) {
|
||||
return;
|
||||
} else {
|
||||
throw responseDto.message;
|
||||
}
|
||||
} catch (e, st) {
|
||||
debugPrint("ERROR WHILE REMOVING CAR");
|
||||
debugPrint(e.toString());
|
||||
debugPrint(st.toString());
|
||||
|
||||
rethrow;
|
||||
} finally {
|
||||
await logout(session);
|
||||
}
|
||||
}
|
||||
|
||||
Future<List<Car>> getCars(int teamId) async {
|
||||
df.LoginSession? session;
|
||||
|
||||
try {
|
||||
session = await getSession();
|
||||
df.DocuFrameMacroResponse response =
|
||||
await df.Macro(config: dfConfig, session: session)
|
||||
.execute("_web_getCars", parameter: {"team_id": teamId});
|
||||
|
||||
debugPrint(teamId.toString());
|
||||
|
||||
Map<String, dynamic> responseJson = jsonDecode(response.body!);
|
||||
debugPrint("RESPONSE");
|
||||
debugPrint(responseJson.toString());
|
||||
CarGetResponseDTO responseDto = CarGetResponseDTO.fromJson(responseJson);
|
||||
|
||||
if (responseDto.succeeded == true) {
|
||||
return responseDto.cars!
|
||||
.map((carDto) => Car(id: int.parse(carDto.id), plate: carDto.plate))
|
||||
.toList();
|
||||
} else {
|
||||
throw responseDto.message;
|
||||
}
|
||||
} catch (e, st) {
|
||||
debugPrint("ERROR WHILE FETCHING CARS");
|
||||
debugPrint(e.toString());
|
||||
debugPrint(st.toString());
|
||||
|
||||
rethrow;
|
||||
} finally {
|
||||
await logout(session);
|
||||
}
|
||||
}
|
||||
}
|
||||
309
lib/feature/delivery/detail/bloc/delivery_bloc.dart
Normal file
309
lib/feature/delivery/detail/bloc/delivery_bloc.dart
Normal file
@ -0,0 +1,309 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/dto/discount_add_response.dart';
|
||||
import 'package:hl_lieferservice/dto/discount_update_response.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_event.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_state.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/repository/delivery_repository.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_event.dart';
|
||||
|
||||
import '../../../../dto/discount_remove_response.dart';
|
||||
import '../../../../model/article.dart';
|
||||
import '../../../../model/delivery.dart' as model;
|
||||
|
||||
class DeliveryBloc extends Bloc<DeliveryEvent, DeliveryState> {
|
||||
OperationBloc opBloc;
|
||||
DeliveryRepository repository;
|
||||
|
||||
DeliveryBloc({required this.opBloc, required this.repository})
|
||||
: super(DeliveryInitial()) {
|
||||
on<UnscanArticleEvent>(_unscan);
|
||||
on<ResetScanAmountEvent>(_resetAmount);
|
||||
on<LoadDeliveryEvent>(_load);
|
||||
on<AddDiscountEvent>(_addDiscount);
|
||||
on<RemoveDiscountEvent>(_removeDiscount);
|
||||
on<UpdateDiscountEvent>(_updateDiscount);
|
||||
on<UpdateDeliveryOption>(_updateDeliveryOptions);
|
||||
on<UpdateSelectedPaymentMethod>(_updatePayment);
|
||||
}
|
||||
|
||||
void _updatePayment(
|
||||
UpdateSelectedPaymentMethod event,
|
||||
Emitter<DeliveryState> emit,
|
||||
) {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is DeliveryLoaded) {
|
||||
emit(
|
||||
DeliveryLoaded(
|
||||
delivery: currentState.delivery.copyWith(payment: event.payment),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _updateDeliveryOptions(
|
||||
UpdateDeliveryOption event,
|
||||
Emitter<DeliveryState> emit,
|
||||
) {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is DeliveryLoaded) {
|
||||
List<model.DeliveryOption> options =
|
||||
currentState.delivery.options.map((option) {
|
||||
if (option.key == event.key) {
|
||||
return option.copyWith(value: event.value.toString());
|
||||
}
|
||||
|
||||
return option;
|
||||
}).toList();
|
||||
|
||||
emit(
|
||||
DeliveryLoaded(
|
||||
delivery: currentState.delivery.copyWith(options: options),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _updateDiscount(
|
||||
UpdateDiscountEvent event,
|
||||
Emitter<DeliveryState> emit,
|
||||
) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is DeliveryLoaded) {
|
||||
DiscountUpdateResponseDTO response = await repository.updateDiscount(
|
||||
event.deliveryId,
|
||||
event.reason,
|
||||
event.value,
|
||||
);
|
||||
|
||||
model.Delivery delivery = currentState.delivery;
|
||||
|
||||
if (response.values?.receipt != null) {
|
||||
delivery.totalNetValue = response.values!.receipt.net;
|
||||
delivery.totalGrossValue = response.values!.receipt.gross;
|
||||
}
|
||||
|
||||
String discountArticleNumber = delivery.discount!.article.articleNumber;
|
||||
delivery.discount = model.Discount(
|
||||
article:
|
||||
response.values?.article != null
|
||||
? Article.fromDTO(response.values!.article)
|
||||
: delivery.discount!.article,
|
||||
note:
|
||||
response.values?.note != null
|
||||
? response.values!.note.noteDescription
|
||||
: delivery.discount!.note,
|
||||
noteId:
|
||||
response.values?.note != null
|
||||
? response.values!.note.rowId
|
||||
: delivery.discount!.noteId,
|
||||
);
|
||||
|
||||
delivery.articles = [
|
||||
...delivery.articles.where(
|
||||
(article) => article.articleNumber != discountArticleNumber,
|
||||
),
|
||||
delivery.discount!.article,
|
||||
];
|
||||
|
||||
emit(currentState.copyWith(delivery));
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
}
|
||||
} catch (e, st) {
|
||||
debugPrint(
|
||||
"Fehler beim Hinzufügen eins Discounts zur Lieferung: ${event.deliveryId}:",
|
||||
);
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Hinzufügen des Discounts: $e"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _removeDiscount(
|
||||
RemoveDiscountEvent event,
|
||||
Emitter<DeliveryState> emit,
|
||||
) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is DeliveryLoaded) {
|
||||
model.Delivery delivery = currentState.delivery;
|
||||
|
||||
DiscountRemoveResponseDTO response = await repository.removeDiscount(
|
||||
event.deliveryId,
|
||||
);
|
||||
|
||||
delivery.articles =
|
||||
delivery.articles
|
||||
.where(
|
||||
(article) =>
|
||||
article.internalId !=
|
||||
delivery.discount?.article.internalId,
|
||||
)
|
||||
.toList();
|
||||
|
||||
delivery.discount = null;
|
||||
delivery.totalGrossValue = response.receipt.gross;
|
||||
delivery.totalNetValue = response.receipt.net;
|
||||
|
||||
emit(currentState.copyWith(delivery));
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
}
|
||||
} catch (e, st) {
|
||||
debugPrint(
|
||||
"Fehler beim Löschen des Discounts der Lieferung: ${event.deliveryId}:",
|
||||
);
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Löschen des Discounts: $e"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _addDiscount(AddDiscountEvent event, Emitter<DeliveryState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is DeliveryLoaded) {
|
||||
DiscountAddResponseDTO response = await repository.addDiscount(
|
||||
event.deliveryId,
|
||||
event.reason,
|
||||
event.value,
|
||||
);
|
||||
|
||||
model.Delivery delivery = currentState.delivery;
|
||||
delivery.totalNetValue = response.values.receipt.net;
|
||||
delivery.totalGrossValue = response.values.receipt.gross;
|
||||
|
||||
delivery.discount = model.Discount(
|
||||
article: Article.fromDTO(response.values.article),
|
||||
note: response.values.note.noteDescription,
|
||||
noteId: response.values.note.rowId,
|
||||
);
|
||||
|
||||
delivery.articles = [...delivery.articles, delivery.discount!.article];
|
||||
|
||||
emit(currentState.copyWith(delivery));
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
}
|
||||
} catch (e, st) {
|
||||
debugPrint(
|
||||
"Fehler beim Hinzufügen eins Discounts zur Lieferung: ${event.deliveryId}:",
|
||||
);
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
|
||||
opBloc.add(
|
||||
FailOperation(message: "Fehler beim Hinzufügen des Discounts: $e"),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _load(LoadDeliveryEvent event, Emitter<DeliveryState> emit) async {
|
||||
debugPrint("Discount; ${event.delivery.discount?.note}");
|
||||
emit(DeliveryLoaded(delivery: event.delivery));
|
||||
}
|
||||
|
||||
void _unscan(UnscanArticleEvent event, Emitter<DeliveryState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
String? noteId = await repository.unscan(
|
||||
event.articleId,
|
||||
event.newAmount,
|
||||
event.reason,
|
||||
);
|
||||
|
||||
if (noteId != null) {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is DeliveryLoaded) {
|
||||
Article article = currentState.delivery.articles.firstWhere(
|
||||
(article) => article.internalId == int.parse(event.articleId),
|
||||
);
|
||||
|
||||
article.removeNoteId = noteId;
|
||||
article.scannedRemovedAmount += event.newAmount;
|
||||
article.scannedAmount -= event.newAmount;
|
||||
|
||||
List<Article> articles = [
|
||||
...currentState.delivery.articles.where(
|
||||
(article) => article.internalId != int.parse(event.articleId),
|
||||
),
|
||||
article,
|
||||
];
|
||||
currentState.delivery.articles = articles;
|
||||
|
||||
emit.call(currentState.copyWith(currentState.delivery));
|
||||
}
|
||||
}
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Unscan des Artikels: ${event.articleId}:");
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
|
||||
opBloc.add(FailOperation(message: "Fehler beim Unscan des Artikels: $e"));
|
||||
}
|
||||
}
|
||||
|
||||
void _resetAmount(
|
||||
ResetScanAmountEvent event,
|
||||
Emitter<DeliveryState> emit,
|
||||
) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
final currentState = state;
|
||||
await repository.resetScan(event.articleId);
|
||||
|
||||
if (currentState is DeliveryLoaded) {
|
||||
Article article = currentState.delivery.articles.firstWhere(
|
||||
(article) => article.internalId == int.parse(event.articleId),
|
||||
);
|
||||
|
||||
article.removeNoteId = null;
|
||||
article.scannedRemovedAmount = 0;
|
||||
article.scannedAmount = article.amount;
|
||||
|
||||
List<Article> articles = [
|
||||
...currentState.delivery.articles.where(
|
||||
(article) => article.internalId != int.parse(event.articleId),
|
||||
),
|
||||
article,
|
||||
];
|
||||
currentState.delivery.articles = articles;
|
||||
|
||||
emit.call(currentState.copyWith(currentState.delivery));
|
||||
}
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Unscan des Artikels: ${event.articleId}:");
|
||||
debugPrint("$e");
|
||||
debugPrint("$st");
|
||||
|
||||
opBloc.add(FailOperation(message: "Fehler beim Zurücksetzen: $e"));
|
||||
}
|
||||
}
|
||||
}
|
||||
71
lib/feature/delivery/detail/bloc/delivery_event.dart
Normal file
71
lib/feature/delivery/detail/bloc/delivery_event.dart
Normal file
@ -0,0 +1,71 @@
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
import 'package:hl_lieferservice/model/tour.dart';
|
||||
|
||||
abstract class DeliveryEvent {}
|
||||
|
||||
class LoadDeliveryEvent extends DeliveryEvent {
|
||||
LoadDeliveryEvent({required this.delivery});
|
||||
|
||||
Delivery delivery;
|
||||
}
|
||||
|
||||
class UnscanArticleEvent extends DeliveryEvent {
|
||||
UnscanArticleEvent({
|
||||
required this.articleId,
|
||||
required this.newAmount,
|
||||
required this.reason,
|
||||
});
|
||||
|
||||
String articleId;
|
||||
String reason;
|
||||
int newAmount;
|
||||
}
|
||||
|
||||
class ResetScanAmountEvent extends DeliveryEvent {
|
||||
ResetScanAmountEvent({required this.articleId});
|
||||
|
||||
String articleId;
|
||||
}
|
||||
|
||||
class AddDiscountEvent extends DeliveryEvent {
|
||||
AddDiscountEvent({
|
||||
required this.deliveryId,
|
||||
required this.value,
|
||||
required this.reason,
|
||||
});
|
||||
|
||||
String deliveryId;
|
||||
String reason;
|
||||
int value;
|
||||
}
|
||||
|
||||
class RemoveDiscountEvent extends DeliveryEvent {
|
||||
RemoveDiscountEvent({required this.deliveryId});
|
||||
|
||||
String deliveryId;
|
||||
}
|
||||
|
||||
class UpdateDiscountEvent extends DeliveryEvent {
|
||||
UpdateDiscountEvent({
|
||||
required this.deliveryId,
|
||||
required this.value,
|
||||
required this.reason,
|
||||
});
|
||||
|
||||
String deliveryId;
|
||||
String? reason;
|
||||
int? value;
|
||||
}
|
||||
|
||||
class UpdateDeliveryOption extends DeliveryEvent {
|
||||
UpdateDeliveryOption({required this.key, required this.value});
|
||||
|
||||
String key;
|
||||
dynamic value;
|
||||
}
|
||||
|
||||
class UpdateSelectedPaymentMethod extends DeliveryEvent {
|
||||
UpdateSelectedPaymentMethod({required this.payment});
|
||||
|
||||
Payment payment;
|
||||
}
|
||||
15
lib/feature/delivery/detail/bloc/delivery_state.dart
Normal file
15
lib/feature/delivery/detail/bloc/delivery_state.dart
Normal file
@ -0,0 +1,15 @@
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
|
||||
abstract class DeliveryState {}
|
||||
|
||||
class DeliveryInitial extends DeliveryState {}
|
||||
|
||||
class DeliveryLoaded extends DeliveryState {
|
||||
DeliveryLoaded({required this.delivery});
|
||||
|
||||
Delivery delivery;
|
||||
|
||||
DeliveryLoaded copyWith(Delivery? delivery) {
|
||||
return DeliveryLoaded(delivery: delivery ?? this.delivery);
|
||||
}
|
||||
}
|
||||
188
lib/feature/delivery/detail/bloc/note_bloc.dart
Normal file
188
lib/feature/delivery/detail/bloc/note_bloc.dart
Normal file
@ -0,0 +1,188 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart';
|
||||
import 'package:hl_lieferservice/widget/operations/bloc/operation_event.dart';
|
||||
|
||||
import 'note_event.dart';
|
||||
import 'note_state.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/repository/note_repository.dart';
|
||||
|
||||
class NoteBloc extends Bloc<NoteEvent, NoteState> {
|
||||
final NoteRepository repository;
|
||||
final OperationBloc opBloc;
|
||||
|
||||
NoteBloc({required this.repository, required this.opBloc})
|
||||
: super(NoteInitial()) {
|
||||
on<LoadNote>(_load);
|
||||
on<AddNote>(_add);
|
||||
on<EditNote>(_edit);
|
||||
on<RemoveNote>(_remove);
|
||||
on<AddImageNote>(_upload);
|
||||
on<RemoveImageNote>(_removeImage);
|
||||
}
|
||||
|
||||
Future<void> _removeImage(
|
||||
RemoveImageNote event,
|
||||
Emitter<NoteState> emit,
|
||||
) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
final currentState = state;
|
||||
await repository.deleteImage(event.deliveryId, event.objectId);
|
||||
|
||||
if (currentState is NoteLoaded) {
|
||||
emit.call(
|
||||
currentState.copyWith(
|
||||
images:
|
||||
currentState.images
|
||||
.where((image) => image.$1.objectId != event.objectId)
|
||||
.toList(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Löschen des Bildes: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(FailOperation(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _upload(AddImageNote event, Emitter<NoteState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
final currentState = state;
|
||||
Uint8List imageBytes = await event.file.readAsBytes();
|
||||
ImageNote note = await repository.addImage(event.deliveryId, imageBytes);
|
||||
|
||||
if (currentState is NoteLoaded) {
|
||||
emit.call(
|
||||
currentState.copyWith(
|
||||
images: [...currentState.images, (note, imageBytes)],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Hinzufügen des Bildes: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(FailOperation(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _load(LoadNote event, Emitter<NoteState> emit) async {
|
||||
emit.call(NoteLoading());
|
||||
|
||||
try {
|
||||
List<String> urls =
|
||||
event.delivery.images.map((image) => image.url).toList();
|
||||
List<Note> notes = await repository.loadNotes(event.delivery.id);
|
||||
List<NoteTemplate> templates = await repository.loadTemplates();
|
||||
List<Uint8List> images = await repository.loadImages(urls);
|
||||
|
||||
emit.call(
|
||||
NoteLoaded(
|
||||
notes: notes,
|
||||
templates: templates,
|
||||
images: List.generate(
|
||||
images.length,
|
||||
(index) => (event.delivery.images[index], images[index]),
|
||||
),
|
||||
),
|
||||
);
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Herunterladen der Notizen: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(
|
||||
FailOperation(message: "Notizen konnten nicht heruntergeladen werden."),
|
||||
);
|
||||
|
||||
emit.call(NoteLoadingFailed());
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _add(AddNote event, Emitter<NoteState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
final currentState = state;
|
||||
Note note = await repository.addNote(event.deliveryId, event.note);
|
||||
|
||||
if (currentState is NoteLoaded) {
|
||||
List<Note> refreshedNotes = [...currentState.notes, note];
|
||||
emit.call(currentState.copyWith(notes: refreshedNotes));
|
||||
}
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Hinzufügen der Notiz: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(FailOperation(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _edit(EditNote event, Emitter<NoteState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
final currentState = state;
|
||||
await repository.editNote(event.noteId, event.content);
|
||||
|
||||
if (currentState is NoteLoaded) {
|
||||
List<Note> refreshedNotes = [
|
||||
...currentState.notes.where(
|
||||
(note) => note.id != int.parse(event.noteId),
|
||||
),
|
||||
Note(content: event.content, id: int.parse(event.noteId)),
|
||||
];
|
||||
emit.call(currentState.copyWith(notes: refreshedNotes));
|
||||
}
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Hinzufügen der Notiz: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(FailOperation(message: e.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _remove(RemoveNote event, Emitter<NoteState> emit) async {
|
||||
opBloc.add(LoadOperation());
|
||||
|
||||
try {
|
||||
final currentState = state;
|
||||
await repository.deleteNote(event.noteId);
|
||||
|
||||
if (currentState is NoteLoaded) {
|
||||
List<Note> refreshedNotes =
|
||||
currentState.notes
|
||||
.where((note) => note.id != int.parse(event.noteId))
|
||||
.toList();
|
||||
|
||||
emit.call(currentState.copyWith(notes: refreshedNotes));
|
||||
}
|
||||
|
||||
opBloc.add(FinishOperation());
|
||||
} catch (e, st) {
|
||||
debugPrint("Fehler beim Hinzufügen der Notiz: $e");
|
||||
debugPrint(st.toString());
|
||||
|
||||
opBloc.add(
|
||||
FailOperation(message: "Notizen konnte nicht gelöscht werden."),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
44
lib/feature/delivery/detail/bloc/note_event.dart
Normal file
44
lib/feature/delivery/detail/bloc/note_event.dart
Normal file
@ -0,0 +1,44 @@
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
|
||||
abstract class NoteEvent {}
|
||||
|
||||
class LoadNote extends NoteEvent {
|
||||
LoadNote({required this.delivery});
|
||||
|
||||
final Delivery delivery;
|
||||
}
|
||||
|
||||
class AddNote extends NoteEvent {
|
||||
AddNote({required this.note, required this.deliveryId});
|
||||
|
||||
final String note;
|
||||
final String deliveryId;
|
||||
}
|
||||
|
||||
class RemoveNote extends NoteEvent {
|
||||
RemoveNote({required this.noteId});
|
||||
|
||||
final String noteId;
|
||||
}
|
||||
|
||||
class EditNote extends NoteEvent {
|
||||
EditNote({required this.content, required this.noteId});
|
||||
|
||||
final String noteId;
|
||||
final String content;
|
||||
}
|
||||
|
||||
class AddImageNote extends NoteEvent {
|
||||
AddImageNote({required this.file, required this.deliveryId});
|
||||
|
||||
final XFile file;
|
||||
final String deliveryId;
|
||||
}
|
||||
|
||||
class RemoveImageNote extends NoteEvent {
|
||||
RemoveImageNote({required this.objectId, required this.deliveryId});
|
||||
|
||||
final String objectId;
|
||||
final String deliveryId;
|
||||
}
|
||||
35
lib/feature/delivery/detail/bloc/note_state.dart
Normal file
35
lib/feature/delivery/detail/bloc/note_state.dart
Normal file
@ -0,0 +1,35 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
|
||||
abstract class NoteState {}
|
||||
|
||||
class NoteInitial extends NoteState {}
|
||||
|
||||
class NoteLoading extends NoteState {}
|
||||
|
||||
class NoteLoadingFailed extends NoteState {}
|
||||
|
||||
class NoteLoaded extends NoteState {
|
||||
NoteLoaded({
|
||||
required this.notes,
|
||||
required this.templates,
|
||||
required this.images,
|
||||
});
|
||||
|
||||
List<Note> notes;
|
||||
List<NoteTemplate> templates;
|
||||
List<(ImageNote, Uint8List)> images;
|
||||
|
||||
NoteLoaded copyWith({
|
||||
List<Note>? notes,
|
||||
List<NoteTemplate>? templates,
|
||||
List<(ImageNote, Uint8List)>? images,
|
||||
}) {
|
||||
return NoteLoaded(
|
||||
notes: notes ?? this.notes,
|
||||
templates: templates ?? this.templates,
|
||||
images: images ?? this.images,
|
||||
);
|
||||
}
|
||||
}
|
||||
9
lib/feature/delivery/detail/model/note.dart
Normal file
9
lib/feature/delivery/detail/model/note.dart
Normal file
@ -0,0 +1,9 @@
|
||||
import 'package:hl_lieferservice/model/article.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
|
||||
class NoteInformation {
|
||||
NoteInformation({required this.note, this.article});
|
||||
|
||||
Note note;
|
||||
Article? article;
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/presentation/article/article_list_item.dart';
|
||||
import 'package:hl_lieferservice/model/article.dart';
|
||||
|
||||
class ArticleList extends StatefulWidget {
|
||||
const ArticleList({
|
||||
super.key,
|
||||
required this.articles,
|
||||
required this.deliveryId,
|
||||
});
|
||||
|
||||
final List<Article> articles;
|
||||
final String deliveryId;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ArticleListState();
|
||||
}
|
||||
|
||||
class _ArticleListState extends State<ArticleList> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder:
|
||||
(context, index) => ArticleListItem(
|
||||
article: widget.articles[index],
|
||||
deliveryId: widget.deliveryId,
|
||||
),
|
||||
separatorBuilder: (context, index) => const Divider(height: 0),
|
||||
itemCount: widget.articles.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,85 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/presentation/article/article_reset_scan_dialog.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/presentation/article/article_unscan_dialog.dart';
|
||||
import 'package:hl_lieferservice/model/article.dart';
|
||||
|
||||
class ArticleListItem extends StatefulWidget {
|
||||
const ArticleListItem({
|
||||
super.key,
|
||||
required this.article,
|
||||
required this.deliveryId,
|
||||
});
|
||||
|
||||
final Article article;
|
||||
final String deliveryId;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ArticleListItem();
|
||||
}
|
||||
|
||||
class _ArticleListItem extends State<ArticleListItem> {
|
||||
Widget _leading() {
|
||||
int amount = widget.article.getScannedAmount();
|
||||
Color? color;
|
||||
Color? textColor;
|
||||
|
||||
if (amount == 0) {
|
||||
color = Colors.redAccent;
|
||||
textColor = Theme.of(context).colorScheme.onSecondary;
|
||||
}
|
||||
|
||||
return CircleAvatar(
|
||||
backgroundColor: color,
|
||||
child: Text("${amount}x", style: TextStyle(color: textColor)),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget actionButton = IconButton.outlined(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStatePropertyAll(Colors.redAccent),
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => ArticleUnscanDialog(article: widget.article),
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.delete,
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
);
|
||||
|
||||
if (widget.article.unscanned()) {
|
||||
actionButton = IconButton.outlined(
|
||||
style: ButtonStyle(
|
||||
backgroundColor: WidgetStatePropertyAll(Colors.blueAccent),
|
||||
),
|
||||
onPressed: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder:
|
||||
(context) => ResetArticleAmountDialog(article: widget.article),
|
||||
);
|
||||
},
|
||||
icon: Icon(
|
||||
Icons.refresh,
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListTile(
|
||||
tileColor: Theme.of(context).colorScheme.surfaceContainerLowest,
|
||||
title: Text(widget.article.name),
|
||||
leading: _leading(),
|
||||
subtitle: Text("Artikelnr. ${widget.article.articleNumber}"),
|
||||
trailing: actionButton,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_event.dart';
|
||||
|
||||
import '../../../../../model/article.dart';
|
||||
|
||||
class ResetArticleAmountDialog extends StatefulWidget {
|
||||
const ResetArticleAmountDialog({super.key, required this.article});
|
||||
|
||||
final Article article;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ResetArticleAmountDialogState();
|
||||
}
|
||||
|
||||
class _ResetArticleAmountDialogState extends State<ResetArticleAmountDialog> {
|
||||
void _reset() {
|
||||
context.read<DeliveryBloc>().add(
|
||||
ResetScanAmountEvent(articleId: widget.article.internalId.toString()),
|
||||
);
|
||||
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text("Anzahl Artikel zurücksetzen?"),
|
||||
content: SizedBox(
|
||||
height: 120,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Text("Wollen Sie die entfernten Artikel wieder hinzufügen?"),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
FilledButton(
|
||||
onPressed: _reset,
|
||||
child: const Text("Zurücksetzen"),
|
||||
),
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text("Abbrechen"),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,153 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_event.dart';
|
||||
|
||||
import '../../../../../model/article.dart';
|
||||
|
||||
class ArticleUnscanDialog extends StatefulWidget {
|
||||
const ArticleUnscanDialog({super.key, required this.article});
|
||||
|
||||
final Article article;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ArticleUnscanDialogState();
|
||||
}
|
||||
|
||||
class _ArticleUnscanDialogState extends State<ArticleUnscanDialog> {
|
||||
late TextEditingController unscanAmountController;
|
||||
late TextEditingController unscanNoteController;
|
||||
bool isValidText = false;
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
|
||||
void _unscan() {
|
||||
context.read<DeliveryBloc>().add(
|
||||
UnscanArticleEvent(
|
||||
articleId: widget.article.internalId.toString(),
|
||||
newAmount: int.parse(unscanAmountController.text),
|
||||
reason: unscanNoteController.text,
|
||||
),
|
||||
);
|
||||
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
unscanAmountController = TextEditingController(text: "1");
|
||||
unscanNoteController = TextEditingController(text: "");
|
||||
|
||||
unscanNoteController.addListener(() {
|
||||
setState(() {
|
||||
isValidText = _isValid();
|
||||
});
|
||||
});
|
||||
|
||||
unscanAmountController.addListener(() {
|
||||
setState(() {
|
||||
isValidText = _isValid();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
unscanAmountController.dispose();
|
||||
unscanNoteController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
bool _isValid() {
|
||||
return _isAmountValid() && unscanNoteController.text.isNotEmpty;
|
||||
}
|
||||
|
||||
bool _isAmountValid() {
|
||||
final amount = int.tryParse(unscanAmountController.text);
|
||||
return amount != null && amount > 0 && amount <= widget.article.amount;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text("Scan rückgängig machen"),
|
||||
content: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 350,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Text(
|
||||
"Wollen Sie den Scanvorgang des Artikel '${widget.article.name}' rückgängig machen und den Artikel aus der Bestellung entfernen?",
|
||||
),
|
||||
Form(
|
||||
key: _formKey,
|
||||
autovalidateMode: AutovalidateMode.always,
|
||||
child: Column(
|
||||
children: [
|
||||
TextFormField(
|
||||
keyboardType: TextInputType.number,
|
||||
inputFormatters: <TextInputFormatter>[
|
||||
FilteringTextInputFormatter.digitsOnly,
|
||||
],
|
||||
validator: (text) {
|
||||
if (text == null || text.isEmpty) {
|
||||
return "Geben Sie eine Zahl ein";
|
||||
}
|
||||
|
||||
final amount = int.tryParse(text);
|
||||
if (amount == null || amount <= 0) {
|
||||
return "Geben Sie eine gültige Zahl ein";
|
||||
}
|
||||
|
||||
if (amount > widget.article.amount) {
|
||||
return "Maximal ${widget.article.amount} möglich.";
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
controller: unscanAmountController,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Menge zu löschender Artikel",
|
||||
),
|
||||
),
|
||||
TextFormField(
|
||||
controller: unscanNoteController,
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Grund für die Entfernung",
|
||||
),
|
||||
validator: (text) {
|
||||
if (text == null || text.isEmpty) {
|
||||
return "Geben Sie einen Grund an.";
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
FilledButton(
|
||||
onPressed: isValidText ? _unscan : null,
|
||||
child: const Text("Entfernen"),
|
||||
),
|
||||
OutlinedButton(
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
},
|
||||
child: const Text("Abbrechen"),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,186 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:easy_stepper/easy_stepper.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_event.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_state.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/presentation/delivery_sign.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/presentation/steps/step.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart' as model;
|
||||
|
||||
class DeliveryDetail extends StatefulWidget {
|
||||
final model.Delivery delivery;
|
||||
|
||||
const DeliveryDetail({super.key, required this.delivery});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _DeliveryDetailState();
|
||||
}
|
||||
|
||||
class _DeliveryDetailState extends State<DeliveryDetail> {
|
||||
late int _step;
|
||||
late List<EasyStep> _steps;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
// Initialize BLOC
|
||||
context.read<DeliveryBloc>().add(
|
||||
LoadDeliveryEvent(delivery: widget.delivery),
|
||||
);
|
||||
|
||||
// Initialize steps
|
||||
_step = 0;
|
||||
_steps = [
|
||||
EasyStep(
|
||||
icon: const Icon(Icons.info),
|
||||
customTitle: Text("Info", textAlign: TextAlign.center),
|
||||
),
|
||||
EasyStep(
|
||||
icon: const Icon(Icons.book),
|
||||
customTitle: Text("Notizen", textAlign: TextAlign.center),
|
||||
),
|
||||
EasyStep(
|
||||
icon: const Icon(Icons.shopping_cart),
|
||||
customTitle: Text("Artikel/Gutschriften", textAlign: TextAlign.center),
|
||||
),
|
||||
EasyStep(
|
||||
icon: const Icon(Icons.settings),
|
||||
customTitle: Text("Optionen", textAlign: TextAlign.center),
|
||||
),
|
||||
EasyStep(
|
||||
icon: const Icon(Icons.check_box),
|
||||
customTitle: Text(
|
||||
"Überprüfen",
|
||||
textAlign: TextAlign.center,
|
||||
overflow: TextOverflow.clip,
|
||||
),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Widget _stepInfo() {
|
||||
return DecoratedBox(
|
||||
decoration: const BoxDecoration(),
|
||||
child: SizedBox(
|
||||
height: 115,
|
||||
child: EasyStepper(
|
||||
activeStep: _step,
|
||||
showLoadingAnimation: false,
|
||||
activeStepTextColor: Theme.of(context).primaryColor,
|
||||
activeStepBorderType: BorderType.dotted,
|
||||
finishedStepBorderType: BorderType.normal,
|
||||
unreachedStepBorderType: BorderType.normal,
|
||||
activeStepBackgroundColor: Colors.white,
|
||||
borderThickness: 2,
|
||||
internalPadding: 0.0,
|
||||
enableStepTapping: true,
|
||||
stepRadius: 25.0,
|
||||
onStepReached:
|
||||
(index) => {
|
||||
setState(() {
|
||||
_step = index;
|
||||
}),
|
||||
},
|
||||
steps: _steps,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _stepMissingWarning() {
|
||||
return Center(
|
||||
child: Text("Kein Inhalt für den aktuellen Step $_step gefunden."),
|
||||
);
|
||||
}
|
||||
|
||||
void _clickForward() {
|
||||
if (_step < _steps.length) {
|
||||
setState(() {
|
||||
_step += 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _clickBack() {
|
||||
if (_step > 0) {
|
||||
setState(() {
|
||||
_step -= 1;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void _openSignatureView() {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(context) => SignatureView(
|
||||
onSigned: _onSign,
|
||||
customer: widget.delivery.customer,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onSign(Uint8List customer, Uint8List driver) async {
|
||||
|
||||
}
|
||||
|
||||
Widget _stepsNavigation() {
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 90,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
OutlinedButton(
|
||||
onPressed: _step == 0 ? null : _clickBack,
|
||||
child: const Text("zurück"),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 20),
|
||||
child: FilledButton(
|
||||
onPressed: _step == _steps.length - 1 ? _openSignatureView : _clickForward,
|
||||
child:
|
||||
_step == _steps.length - 1
|
||||
? const Text("Unterschreiben")
|
||||
: const Text("weiter"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text("Auslieferungsdetails")),
|
||||
body: BlocBuilder<DeliveryBloc, DeliveryState>(
|
||||
builder: (context, state) {
|
||||
final currentState = state;
|
||||
|
||||
if (currentState is DeliveryLoaded) {
|
||||
return Column(
|
||||
children: [
|
||||
_stepInfo(),
|
||||
const Divider(),
|
||||
Expanded(
|
||||
child:
|
||||
StepFactory().make(_step, currentState.delivery) ??
|
||||
_stepMissingWarning(),
|
||||
),
|
||||
_stepsNavigation(),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
return Container();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
221
lib/feature/delivery/detail/presentation/delivery_discount.dart
Normal file
221
lib/feature/delivery/detail/presentation/delivery_discount.dart
Normal file
@ -0,0 +1,221 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_event.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
|
||||
class DeliveryDiscount extends StatefulWidget {
|
||||
const DeliveryDiscount({
|
||||
super.key,
|
||||
this.discount,
|
||||
required this.disabled,
|
||||
required this.deliveryId,
|
||||
});
|
||||
|
||||
final bool disabled;
|
||||
final Discount? discount;
|
||||
final String deliveryId;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _DeliveryDiscountState();
|
||||
}
|
||||
|
||||
class _DeliveryDiscountState extends State<DeliveryDiscount> {
|
||||
final int stepSize = 10;
|
||||
|
||||
late TextEditingController _reasonController;
|
||||
late bool _isReasonEmpty;
|
||||
late bool _isUpdated;
|
||||
late int _discountValue;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_reasonController = TextEditingController(text: widget.discount?.note);
|
||||
_isReasonEmpty = _reasonController.text.isEmpty;
|
||||
_reasonController.addListener(() {
|
||||
setState(() {
|
||||
_isReasonEmpty = _reasonController.text.isEmpty;
|
||||
});
|
||||
});
|
||||
|
||||
_discountValue =
|
||||
widget.discount?.article.getGrossPrice().floor().abs() ?? 0;
|
||||
|
||||
_isUpdated = _discountValue > 0 && _reasonController.text.isNotEmpty;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
_reasonController.dispose();
|
||||
}
|
||||
|
||||
bool _maximumReached() {
|
||||
return _discountValue >= 150;
|
||||
}
|
||||
|
||||
bool _minimumReached() {
|
||||
return _discountValue <= 0;
|
||||
}
|
||||
|
||||
Widget _incrementDiscount() {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton.filled(
|
||||
onPressed:
|
||||
_minimumReached() || widget.disabled
|
||||
? null
|
||||
: () {
|
||||
setState(() {
|
||||
if (_discountValue - stepSize >= 0) {
|
||||
_discountValue -= stepSize;
|
||||
}
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.remove),
|
||||
style: ButtonStyle(
|
||||
backgroundColor:
|
||||
_minimumReached() || widget.disabled
|
||||
? WidgetStateProperty.all(Colors.grey)
|
||||
: WidgetStateProperty.all(Colors.red),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(5),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"${_discountValue.abs()}€",
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18.0,
|
||||
),
|
||||
),
|
||||
const Text("max. 150€", style: TextStyle(fontSize: 10.0)),
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton.filled(
|
||||
onPressed:
|
||||
_maximumReached() || widget.disabled
|
||||
? null
|
||||
: () {
|
||||
setState(() {
|
||||
_discountValue += stepSize;
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.add),
|
||||
style: ButtonStyle(
|
||||
backgroundColor:
|
||||
_maximumReached() || widget.disabled
|
||||
? WidgetStateProperty.all(Colors.grey)
|
||||
: WidgetStateProperty.all(Colors.green),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _resetValues() async {
|
||||
setState(() {
|
||||
_discountValue = 0;
|
||||
_reasonController.clear();
|
||||
_isUpdated = false;
|
||||
});
|
||||
|
||||
context.read<DeliveryBloc>().add(
|
||||
RemoveDiscountEvent(deliveryId: widget.deliveryId),
|
||||
);
|
||||
}
|
||||
|
||||
void _updateValues() async {
|
||||
if (_isUpdated) {
|
||||
context.read<DeliveryBloc>().add(
|
||||
UpdateDiscountEvent(
|
||||
deliveryId: widget.deliveryId,
|
||||
value: _discountValue,
|
||||
reason: _reasonController.text,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
context.read<DeliveryBloc>().add(
|
||||
AddDiscountEvent(
|
||||
deliveryId: widget.deliveryId,
|
||||
value: _discountValue,
|
||||
reason: _reasonController.text,
|
||||
),
|
||||
);
|
||||
|
||||
setState(() {
|
||||
setState(() {
|
||||
_isUpdated = true;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Form(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
const Text(
|
||||
"Betrag:",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
_incrementDiscount(),
|
||||
const Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: const Text(
|
||||
"Begründung:",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 20),
|
||||
child: TextFormField(
|
||||
controller: _reasonController,
|
||||
validator: (text) {
|
||||
if (text == null || text.isEmpty) {
|
||||
return "Begründung für Gutschrift notwendig.";
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
),
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(right: 10),
|
||||
child: FilledButton(
|
||||
onPressed:
|
||||
!_isReasonEmpty && _discountValue > 0
|
||||
? _updateValues
|
||||
: null,
|
||||
child: const Text("Speichern"),
|
||||
),
|
||||
),
|
||||
OutlinedButton(
|
||||
onPressed: _discountValue > 0 ? _resetValues : null,
|
||||
child: const Text("Gutschrift entfernen"),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_event.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart' as model;
|
||||
|
||||
class DeliveryOptionsView extends StatefulWidget {
|
||||
const DeliveryOptionsView({super.key, required this.options});
|
||||
|
||||
final List<model.DeliveryOption> options;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _DeliveryOptionsViewState();
|
||||
}
|
||||
|
||||
class _DeliveryOptionsViewState extends State<DeliveryOptionsView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(covariant DeliveryOptionsView oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
}
|
||||
|
||||
void _update(model.DeliveryOption option, dynamic value) {
|
||||
context.read<DeliveryBloc>().add(
|
||||
UpdateDeliveryOption(key: option.key, value: value),
|
||||
);
|
||||
}
|
||||
|
||||
List<Widget> _options() {
|
||||
List<Widget> boolOptions =
|
||||
widget.options.where((option) => !option.numerical).map((option) {
|
||||
return CheckboxListTile(
|
||||
value: option.getValue() as bool,
|
||||
onChanged: (value) => _update(option, value),
|
||||
title: Text(option.display),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
List<Widget> numericalOptions =
|
||||
widget.options.where((option) => option.numerical).map((option) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(15),
|
||||
child: TextFormField(
|
||||
decoration: InputDecoration(labelText: option.display),
|
||||
initialValue: option.getValue().toString(),
|
||||
keyboardType: TextInputType.number,
|
||||
onTapOutside: (event) => FocusScope.of(context).unfocus(),
|
||||
onChanged: (value) => _update(option, value),
|
||||
),
|
||||
);
|
||||
}).toList();
|
||||
|
||||
return [
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 5),
|
||||
child: Text(
|
||||
"Auswählbare Optionen",
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
...boolOptions,
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Text(
|
||||
"Zahlenwerte",
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
...numericalOptions,
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: ListView(children: _options()),
|
||||
);
|
||||
}
|
||||
}
|
||||
164
lib/feature/delivery/detail/presentation/delivery_sign.dart
Normal file
164
lib/feature/delivery/detail/presentation/delivery_sign.dart
Normal file
@ -0,0 +1,164 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:hl_lieferservice/model/customer.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:signature/signature.dart';
|
||||
|
||||
class SignatureView extends StatefulWidget {
|
||||
const SignatureView({
|
||||
super.key,
|
||||
required this.onSigned,
|
||||
required this.customer,
|
||||
});
|
||||
|
||||
final Customer customer;
|
||||
|
||||
/// Callback that is called when the user has signed.
|
||||
/// The parameter stores the path to the image file of the signature.
|
||||
final void Function(Uint8List customerSignaturePng, Uint8List driverSignaturePng) onSigned;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _SignatureViewState();
|
||||
}
|
||||
|
||||
class _SignatureViewState extends State<SignatureView> {
|
||||
final SignatureController _customerController = SignatureController(
|
||||
penStrokeWidth: 5,
|
||||
penColor: Colors.black,
|
||||
exportBackgroundColor: Colors.white,
|
||||
);
|
||||
|
||||
final SignatureController _driverController = SignatureController(
|
||||
penStrokeWidth: 5,
|
||||
penColor: Colors.black,
|
||||
exportBackgroundColor: Colors.white,
|
||||
);
|
||||
|
||||
bool _isDriverSigning = false;
|
||||
bool _customerAccepted = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_customerController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget _signatureField() {
|
||||
return Signature(
|
||||
controller: _isDriverSigning ? _driverController : _customerController,
|
||||
backgroundColor: Colors.white,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String formattedDate = DateFormat("dd.MM.yyyy").format(DateTime.now());
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title:
|
||||
!_isDriverSigning
|
||||
? const Text("Unterschrift des Kunden")
|
||||
: const Text("Unterschrift des Fahrers"),
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: DecoratedBox(
|
||||
decoration: const BoxDecoration(color: Colors.white),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"Lieferung an: ${widget.customer.name}",
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
Expanded(child: _signatureField()),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(),
|
||||
Text(
|
||||
"${widget.customer.address.city}, den $formattedDate",
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
!_isDriverSigning
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: 25.0, bottom: 25.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Checkbox(
|
||||
value: _customerAccepted,
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
_customerAccepted = value!;
|
||||
});
|
||||
},
|
||||
),
|
||||
const Flexible(
|
||||
child: Text(
|
||||
"Ich bestätige, dass ich die Ware im ordnungsgemäßen Zustand erhalten habe und, dass die Aufstell- und Einbauarbeiten korrekt durchgeführt wurden.",
|
||||
overflow: TextOverflow.fade,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 25.0, bottom: 25.0),
|
||||
child: Center(
|
||||
child: FilledButton(
|
||||
onPressed:
|
||||
!_customerAccepted
|
||||
? null
|
||||
: () async {
|
||||
if (!_isDriverSigning) {
|
||||
setState(() {
|
||||
_isDriverSigning = true;
|
||||
});
|
||||
} else {
|
||||
widget.onSigned(
|
||||
(await _customerController.toPngBytes())!,
|
||||
(await _driverController.toPngBytes())!,
|
||||
);
|
||||
}
|
||||
},
|
||||
child:
|
||||
!_isDriverSigning
|
||||
? const Text("Weiter")
|
||||
: const Text("Absenden"),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
182
lib/feature/delivery/detail/presentation/delivery_summary.dart
Normal file
182
lib/feature/delivery/detail/presentation/delivery_summary.dart
Normal file
@ -0,0 +1,182 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/delivery_event.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/overview/bloc/tour_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/overview/bloc/tour_state.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
|
||||
import '../../../../model/tour.dart';
|
||||
|
||||
class DeliverySummary extends StatefulWidget {
|
||||
const DeliverySummary({super.key, required this.delivery});
|
||||
|
||||
final Delivery delivery;
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _DeliverySummaryState();
|
||||
}
|
||||
|
||||
class _DeliverySummaryState extends State<DeliverySummary> {
|
||||
late List<Payment> _paymentMethods;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
final tourState = context.read<TourBloc>().state as TourLoaded;
|
||||
_paymentMethods = [
|
||||
widget.delivery.payment,
|
||||
...tourState.tour.paymentMethods,
|
||||
];
|
||||
}
|
||||
|
||||
Widget _deliveredArticles() {
|
||||
List<Widget> items =
|
||||
widget.delivery
|
||||
.getDeliveredArticles()
|
||||
.map(
|
||||
(article) => DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
child: ListTile(
|
||||
title: Text(article.name),
|
||||
subtitle: Text("Artikelnr. ${article.articleNumber}"),
|
||||
trailing: Text(
|
||||
"${article.scannable ? article.getGrossPriceScanned().toStringAsFixed(2) : article.getGrossPrice().toStringAsFixed(2)}€",
|
||||
),
|
||||
leading: CircleAvatar(
|
||||
child: Text(
|
||||
"${article.scannable ? article.scannedAmount : article.amount}x",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
items.add(
|
||||
DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
child: ListTile(
|
||||
title: const Text(
|
||||
"Gesamtsumme:",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
trailing: Text(
|
||||
"${widget.delivery.getGrossPrice().toStringAsFixed(2)}€",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return ListView(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
children: items,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _paymentOptions() {
|
||||
List<DropdownMenuEntry> entries =
|
||||
_paymentMethods
|
||||
.map(
|
||||
(payment) => DropdownMenuEntry(
|
||||
value: payment.id,
|
||||
label: "${payment.description} (${payment.shortcode})",
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
|
||||
debugPrint(widget.delivery.payment.id);
|
||||
|
||||
return DropdownMenu(
|
||||
dropdownMenuEntries: entries,
|
||||
initialSelection: widget.delivery.payment.id,
|
||||
onSelected: (id) {
|
||||
context.read<DeliveryBloc>().add(
|
||||
UpdateSelectedPaymentMethod(
|
||||
payment: _paymentMethods.firstWhere(
|
||||
(payment) => payment.id == id,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _payment() {
|
||||
return _paymentOptions();
|
||||
}
|
||||
|
||||
Widget _paymentDone() {
|
||||
return DecoratedBox(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: const Text(
|
||||
"Bei Bestellung bezahlt:",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
trailing: Text("${widget.delivery.prepayment.toStringAsFixed(2)}€"),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text(
|
||||
"Offener Betrag:",
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
trailing: Text(
|
||||
"${widget.delivery.getOpenPrice().toStringAsFixed(2)}€",
|
||||
style: TextStyle(fontWeight: FontWeight.w900, color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final insets = EdgeInsets.all(10);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: ListView(
|
||||
children: [
|
||||
Text(
|
||||
"Ausgelieferte Artikel",
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
|
||||
Padding(padding: insets, child: _deliveredArticles()),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Text(
|
||||
"Geleistete Zahlung",
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
|
||||
Padding(padding: insets, child: _paymentDone()),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10),
|
||||
child: Text(
|
||||
"Zahlungsmethode",
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
|
||||
Padding(padding: insets, child: _payment()),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,151 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/note_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/note_event.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
|
||||
class NoteAddDialog extends StatefulWidget {
|
||||
final String delivery;
|
||||
final List<NoteTemplate> templates;
|
||||
|
||||
const NoteAddDialog({
|
||||
super.key,
|
||||
required this.delivery,
|
||||
required this.templates,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _NoteAddDialogState();
|
||||
}
|
||||
|
||||
class _NoteAddDialogState extends State<NoteAddDialog> {
|
||||
final _noteController = TextEditingController();
|
||||
final _noteSelectionController = TextEditingController();
|
||||
late FocusNode _noteFieldFocusNode;
|
||||
bool _isCustomNotesEmpty = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_noteFieldFocusNode = FocusNode();
|
||||
|
||||
_noteController.addListener(() {
|
||||
setState(() {
|
||||
_isCustomNotesEmpty = _noteController.text.isEmpty;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void _onSave() {
|
||||
String content = _noteController.text;
|
||||
|
||||
if (_noteSelectionController.text.isNotEmpty) {
|
||||
NoteTemplate template = widget.templates.firstWhere(
|
||||
(note) => note.title == _noteSelectionController.text,
|
||||
);
|
||||
|
||||
content = template.content;
|
||||
}
|
||||
|
||||
context.read<NoteBloc>().add(
|
||||
AddNote(note: content, deliveryId: widget.delivery),
|
||||
);
|
||||
|
||||
Navigator.pop(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.75,
|
||||
height: MediaQuery.of(context).size.height * 0.45,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"Notiz hinzufügen",
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
icon: Icon(Icons.close),
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 10.0, top: 20),
|
||||
child: DropdownMenu(
|
||||
controller: _noteSelectionController,
|
||||
onSelected: (int? value) {
|
||||
setState(() {
|
||||
_noteSelectionController.text =
|
||||
widget.templates[value!].title;
|
||||
});
|
||||
},
|
||||
enabled: _isCustomNotesEmpty,
|
||||
width: double.infinity,
|
||||
label: const Text("Notiz auswählen"),
|
||||
dropdownMenuEntries:
|
||||
widget.templates
|
||||
.mapIndexed(
|
||||
(i, note) =>
|
||||
DropdownMenuEntry(value: i, label: note.title),
|
||||
)
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: 0.0, bottom: 0.0),
|
||||
child: Text("oder"),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10.0, bottom: 20.0),
|
||||
child: TextFormField(
|
||||
controller: _noteController,
|
||||
enabled: _noteSelectionController.text.isEmpty,
|
||||
focusNode: _noteFieldFocusNode,
|
||||
decoration: const InputDecoration(
|
||||
labelText: "Eigene Notiz",
|
||||
border: OutlineInputBorder(),
|
||||
),
|
||||
minLines: 3,
|
||||
maxLines: 5,
|
||||
),
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
FilledButton(
|
||||
onPressed:
|
||||
_noteSelectionController.text.isNotEmpty ||
|
||||
_noteController.text.isNotEmpty
|
||||
? _onSave
|
||||
: null,
|
||||
child: const Text("Hinzufügen"),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10.0),
|
||||
child: OutlinedButton(
|
||||
onPressed: null,
|
||||
child: const Text("Zurücksetzen"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/note_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/note_event.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
|
||||
class NoteEditDialog extends StatefulWidget {
|
||||
final Note note;
|
||||
|
||||
const NoteEditDialog({super.key, required this.note});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _NoteEditDialogState();
|
||||
}
|
||||
|
||||
class _NoteEditDialogState extends State<NoteEditDialog> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
late TextEditingController _editController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_editController = TextEditingController(text: widget.note.content);
|
||||
}
|
||||
|
||||
void _onEdit(BuildContext context) {
|
||||
context.read<NoteBloc>().add(
|
||||
EditNote(
|
||||
content: _editController.text,
|
||||
noteId: widget.note.id.toString(),
|
||||
),
|
||||
);
|
||||
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
child: SizedBox(
|
||||
width: MediaQuery.of(context).size.width * 0.75,
|
||||
height: MediaQuery.of(context).size.height * 0.32,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
children: [
|
||||
Text(
|
||||
"Notiz bearbeiten",
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 15),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
TextFormField(
|
||||
onTapOutside: (event) {
|
||||
FocusScope.of(context).unfocus();
|
||||
},
|
||||
decoration: InputDecoration(label: const Text("Notiz")),
|
||||
controller: _editController,
|
||||
minLines: 4,
|
||||
maxLines: 8,
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 20),
|
||||
child: Row(
|
||||
children: [
|
||||
FilledButton(
|
||||
onPressed: () {
|
||||
_onEdit(context);
|
||||
},
|
||||
child: const Text("Bearbeiten"),
|
||||
),
|
||||
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 10),
|
||||
child: OutlinedButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text("Abbrechen"),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:carousel_slider/carousel_slider.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/note_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/note_event.dart';
|
||||
import 'package:hl_lieferservice/model/delivery.dart';
|
||||
|
||||
class NoteImageOverview extends StatefulWidget {
|
||||
final List<(ImageNote, Uint8List)> images;
|
||||
final String deliveryId;
|
||||
|
||||
const NoteImageOverview({
|
||||
super.key,
|
||||
required this.images,
|
||||
required this.deliveryId,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _NoteImageOverviewState();
|
||||
}
|
||||
|
||||
class _NoteImageOverviewState extends State<NoteImageOverview> {
|
||||
int? _imageDeleting;
|
||||
|
||||
void _onRemoveImage(int index) {
|
||||
ImageNote note = widget.images[index].$1;
|
||||
|
||||
context.read<NoteBloc>().add(
|
||||
RemoveImageNote(objectId: note.objectId, deliveryId: widget.deliveryId),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImageCarousel() {
|
||||
return CarouselSlider(
|
||||
options: CarouselOptions(
|
||||
height: 300.0,
|
||||
aspectRatio: 2.0,
|
||||
enableInfiniteScroll: false,
|
||||
),
|
||||
items:
|
||||
widget.images.mapIndexed((index, data) {
|
||||
ImageNote note = data.$1;
|
||||
Uint8List bytes = data.$2;
|
||||
|
||||
return Builder(
|
||||
builder: (BuildContext context) {
|
||||
return Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Image.memory(
|
||||
bytes,
|
||||
fit: BoxFit.fill,
|
||||
width: 1920.0,
|
||||
height: 1090.0,
|
||||
),
|
||||
),
|
||||
_imageDeleting == index
|
||||
? Stack(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(15.0),
|
||||
child: Container(
|
||||
color: Colors.black.withValues(alpha: 0.5),
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: CircularProgressIndicator(
|
||||
backgroundColor:
|
||||
Theme.of(context).colorScheme.onSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Container(),
|
||||
Positioned(
|
||||
right: 0.0,
|
||||
top: 0.0,
|
||||
child: CircleAvatar(
|
||||
radius: 20,
|
||||
child: IconButton.filled(
|
||||
onPressed:
|
||||
!(_imageDeleting == index)
|
||||
? () {
|
||||
_onRemoveImage(index);
|
||||
}
|
||||
: null,
|
||||
icon: const Icon(Icons.delete, color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.images.isEmpty
|
||||
? const Center(child: Text("Noch keine Bilder hochgeladen"))
|
||||
: _buildImageCarousel();
|
||||
}
|
||||
}
|
||||
30
lib/feature/delivery/detail/presentation/note/note_list.dart
Normal file
30
lib/feature/delivery/detail/presentation/note/note_list.dart
Normal file
@ -0,0 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/model/note.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/presentation/note/note_list_item.dart';
|
||||
|
||||
class NoteList extends StatelessWidget {
|
||||
final List<NoteInformation> notes;
|
||||
final String deliveryId;
|
||||
|
||||
const NoteList({super.key, required this.notes, required this.deliveryId});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (notes.isEmpty) {
|
||||
return const Center(child: Text("keine Notizen vorhanden"));
|
||||
}
|
||||
|
||||
return ListView.separated(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemBuilder:
|
||||
(context, index) => NoteListItem(
|
||||
note: notes[index],
|
||||
deliveryId: deliveryId,
|
||||
index: index,
|
||||
),
|
||||
separatorBuilder: (context, index) => const Divider(height: 0),
|
||||
itemCount: notes.length,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/note_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/bloc/note_event.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/model/note.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/detail/presentation/note/note_edit_dialog.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/overview/bloc/tour_bloc.dart';
|
||||
import 'package:hl_lieferservice/feature/delivery/overview/bloc/tour_state.dart';
|
||||
|
||||
import '../../../../../model/delivery.dart';
|
||||
|
||||
class NoteListItem extends StatelessWidget {
|
||||
final NoteInformation note;
|
||||
final String deliveryId;
|
||||
final int index;
|
||||
|
||||
const NoteListItem({
|
||||
super.key,
|
||||
required this.note,
|
||||
required this.deliveryId,
|
||||
required this.index,
|
||||
});
|
||||
|
||||
void _onDelete(BuildContext context) {
|
||||
context.read<NoteBloc>().add(RemoveNote(noteId: note.note.id.toString()));
|
||||
}
|
||||
|
||||
Widget? _subtitle(BuildContext context) {
|
||||
String discountArticleId =
|
||||
(context.read<TourBloc>().state as TourLoaded)
|
||||
.tour
|
||||
.discountArticleNumber;
|
||||
|
||||
if (note.article != null && note.article?.articleNumber == discountArticleId) {
|
||||
return const Text("Begründung der Gutschrift");
|
||||
}
|
||||
|
||||
return note.article != null ? Text(note.article!.name) : null;
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: ListTile(
|
||||
title: Text(note.note.content),
|
||||
subtitle: _subtitle(context),
|
||||
tileColor: Theme.of(context).colorScheme.surfaceContainerLowest,
|
||||
leading: CircleAvatar(child: Text("${index + 1}")),
|
||||
trailing: PopupMenuButton(
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
onTap: () {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (context) => NoteEditDialog(note: note.note),
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.edit, color: Colors.blueAccent),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
child: const Text("Editieren"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
onTap: () {
|
||||
_onDelete(context);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.delete, color: Colors.redAccent),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 5),
|
||||
child: const Text("Löschen"),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user