diff --git a/.idea/runConfigurations/main_dart.xml b/.idea/runConfigurations/main_dart.xml
index aab7b5cd8..934fdfcff 100644
--- a/.idea/runConfigurations/main_dart.xml
+++ b/.idea/runConfigurations/main_dart.xml
@@ -1,6 +1,6 @@
-
+
-
+
\ No newline at end of file
diff --git a/lib/data/models/serializers.dart b/lib/data/models/serializers.dart
index 28e2f7a9f..dd4a48ceb 100644
--- a/lib/data/models/serializers.dart
+++ b/lib/data/models/serializers.dart
@@ -23,6 +23,8 @@ import 'package:invoiceninja_flutter/redux/ui/ui_state.dart';
import 'package:invoiceninja_flutter/redux/invoice/invoice_state.dart';
// STARTER: import - do not remove comment
+import 'package:invoiceninja_flutter/redux/vendor/vendor_state.dart';
+
import 'package:invoiceninja_flutter/redux/task/task_state.dart';
import 'package:invoiceninja_flutter/redux/project/project_state.dart';
import 'package:invoiceninja_flutter/redux/payment/payment_state.dart';
@@ -76,6 +78,8 @@ part 'serializers.g.dart';
TimezoneItemResponse,
TimezoneListResponse,
// STARTER: serializers - do not remove comment
+VendorEntity,
+
TaskEntity,
ProjectEntity,
PaymentEntity,
diff --git a/lib/data/models/serializers.g.dart b/lib/data/models/serializers.g.dart
index 0f1120e90..c5c17fa2d 100644
--- a/lib/data/models/serializers.g.dart
+++ b/lib/data/models/serializers.g.dart
@@ -127,6 +127,8 @@ Serializers _$serializers = (new Serializers().toBuilder()
..add(VendorEntity.serializer)
..add(VendorItemResponse.serializer)
..add(VendorListResponse.serializer)
+ ..add(VendorState.serializer)
+ ..add(VendorUIState.serializer)
..addBuilderFactory(
const FullType(BuiltList, const [const FullType(ActivityEntity)]),
() => new ListBuilder())
@@ -363,5 +365,7 @@ Serializers _$serializers = (new Serializers().toBuilder()
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(int), const FullType(ProjectEntity)]), () => new MapBuilder())
..addBuilderFactory(const FullType(BuiltList, const [const FullType(int)]), () => new ListBuilder())
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(int), const FullType(TaskEntity)]), () => new MapBuilder())
+ ..addBuilderFactory(const FullType(BuiltList, const [const FullType(int)]), () => new ListBuilder())
+ ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(int), const FullType(VendorEntity)]), () => new MapBuilder())
..addBuilderFactory(const FullType(BuiltList, const [const FullType(int)]), () => new ListBuilder()))
.build();
diff --git a/lib/data/repositories/vendor_repository.dart b/lib/data/repositories/vendor_repository.dart
new file mode 100644
index 000000000..4d234ff92
--- /dev/null
+++ b/lib/data/repositories/vendor_repository.dart
@@ -0,0 +1,70 @@
+import 'dart:async';
+import 'dart:convert';
+import 'dart:core';
+import 'package:built_collection/built_collection.dart';
+import 'package:invoiceninja_flutter/constants.dart';
+import 'package:invoiceninja_flutter/data/models/serializers.dart';
+import 'package:invoiceninja_flutter/redux/auth/auth_state.dart';
+import 'package:invoiceninja_flutter/data/models/models.dart';
+import 'package:invoiceninja_flutter/data/web_client.dart';
+
+class VendorRepository {
+
+ const VendorRepository({
+ this.webClient = const WebClient(),
+ });
+
+ final WebClient webClient;
+
+ Future loadItem(
+ CompanyEntity company, AuthState auth, int entityId) async {
+ final dynamic response = await webClient.get(
+ '${auth.url}/vendors/$entityId', company.token);
+
+ final VendorItemResponse vendorResponse =
+ serializers.deserializeWith(VendorItemResponse.serializer, response);
+
+ return vendorResponse.data;
+ }
+
+ Future> loadList(
+ CompanyEntity company, AuthState auth, int updatedAt) async {
+ String url = auth.url + '/vendors?';
+
+ if (updatedAt > 0) {
+ url += '&updated_at=${updatedAt - kUpdatedAtBufferSeconds}';
+ }
+
+ final dynamic response = await webClient.get(url, company.token);
+
+ final VendorListResponse vendorResponse =
+ serializers.deserializeWith(VendorListResponse.serializer, response);
+
+ return vendorResponse.data;
+ }
+
+ Future saveData(
+ CompanyEntity company, AuthState auth, VendorEntity vendor,
+ [EntityAction action]) async {
+ final data = serializers.serializeWith(VendorEntity.serializer, vendor);
+ dynamic response;
+
+ if (vendor.isNew) {
+ response = await webClient.post(
+ auth.url + '/vendors',
+ company.token,
+ json.encode(data));
+ } else {
+ var url = auth.url + '/vendors/' + vendor.id.toString();
+ if (action != null) {
+ url += '?action=' + action.toString();
+ }
+ response = await webClient.put(url, company.token, json.encode(data));
+ }
+
+ final VendorItemResponse vendorResponse =
+ serializers.deserializeWith(VendorItemResponse.serializer, response);
+
+ return vendorResponse.data;
+ }
+}
diff --git a/lib/main.dart b/lib/main.dart
index 7f88a5d7f..c1dd59b35 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -43,6 +43,12 @@ import 'package:local_auth/local_auth.dart';
//import 'package:quick_actions/quick_actions.dart';
// STARTER: import - do not remove comment
+import 'package:invoiceninja_flutter/ui/vendor/vendor_screen.dart';
+import 'package:invoiceninja_flutter/ui/vendor/edit/vendor_edit_vm.dart';
+import 'package:invoiceninja_flutter/ui/vendor/view/vendor_view_vm.dart';
+import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart';
+import 'package:invoiceninja_flutter/redux/vendor/vendor_middleware.dart';
+
import 'package:invoiceninja_flutter/ui/task/task_screen.dart';
import 'package:invoiceninja_flutter/ui/task/edit/task_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/task/view/task_view_vm.dart';
@@ -91,6 +97,8 @@ void main() async {
..addAll(createStoreInvoicesMiddleware())
..addAll(createStorePersistenceMiddleware())
// STARTER: middleware - do not remove comment
+..addAll(createStoreVendorsMiddleware())
+
..addAll(createStoreTasksMiddleware())
..addAll(createStoreProjectsMiddleware())
..addAll(createStorePaymentsMiddleware())
@@ -299,6 +307,13 @@ class InvoiceNinjaAppState extends State {
InvoiceEditScreen.route: (context) => InvoiceEditScreen(),
InvoiceEmailScreen.route: (context) => InvoiceEmailScreen(),
// STARTER: routes - do not remove comment
+VendorScreen.route: (context) {
+widget.store.dispatch(LoadVendors());
+return VendorScreen();
+},
+VendorViewScreen.route: (context) => VendorViewScreen(),
+VendorEditScreen.route: (context) => VendorEditScreen(),
+
TaskScreen.route: (context) {
widget.store.dispatch(LoadTasks());
return TaskScreen();
diff --git a/lib/redux/app/app_state.dart b/lib/redux/app/app_state.dart
index 9636b9847..4410abb88 100644
--- a/lib/redux/app/app_state.dart
+++ b/lib/redux/app/app_state.dart
@@ -13,6 +13,8 @@ import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
// STARTER: import - do not remove comment
+import 'package:invoiceninja_flutter/redux/vendor/vendor_state.dart';
+
import 'package:invoiceninja_flutter/redux/task/task_state.dart';
import 'package:invoiceninja_flutter/redux/project/project_state.dart';
@@ -107,6 +109,9 @@ abstract class AppState implements Built {
case EntityType.invoice:
return invoiceUIState;
// STARTER: states switch - do not remove comment
+case EntityType.vendor:
+return vendorUIState;
+
case EntityType.task:
return taskUIState;
@@ -145,6 +150,11 @@ abstract class AppState implements Built {
ListUIState get invoiceListState => uiState.invoiceUIState.listUIState;
// STARTER: state getters - do not remove comment
+VendorState get vendorState => selectedCompanyState.vendorState;
+ListUIState get vendorListState => uiState.vendorUIState.listUIState;
+VendorUIState get vendorUIState => uiState.vendorUIState;
+
+
TaskState get taskState => selectedCompanyState.taskState;
ListUIState get taskListState => uiState.taskUIState.listUIState;
diff --git a/lib/redux/company/company_reducer.dart b/lib/redux/company/company_reducer.dart
index 4e229d962..f4485f0c9 100644
--- a/lib/redux/company/company_reducer.dart
+++ b/lib/redux/company/company_reducer.dart
@@ -9,6 +9,8 @@ import 'package:invoiceninja_flutter/redux/dashboard/dashboard_reducer.dart';
import 'package:invoiceninja_flutter/redux/company/company_actions.dart';
// STARTER: import - do not remove comment
+import 'package:invoiceninja_flutter/redux/vendor/vendor_reducer.dart';
+
import 'package:invoiceninja_flutter/redux/task/task_reducer.dart';
import 'package:invoiceninja_flutter/redux/project/project_reducer.dart';
@@ -29,6 +31,8 @@ CompanyState companyReducer(CompanyState state, dynamic action) {
..productState.replace(productsReducer(state.productState, action))
..invoiceState.replace(invoicesReducer(state.invoiceState, action))
// STARTER: reducer - do not remove comment
+..vendorState.replace(vendorsReducer(state.vendorState, action))
+
..taskState.replace(tasksReducer(state.taskState, action))
..projectState.replace(projectsReducer(state.projectState, action))
..paymentState.replace(paymentsReducer(state.paymentState, action))
diff --git a/lib/redux/company/company_state.dart b/lib/redux/company/company_state.dart
index 518a38f00..6eba752fe 100644
--- a/lib/redux/company/company_state.dart
+++ b/lib/redux/company/company_state.dart
@@ -7,6 +7,8 @@ import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
// STARTER: import - do not remove comment
+import 'package:invoiceninja_flutter/redux/vendor/vendor_state.dart';
+
import 'package:invoiceninja_flutter/redux/task/task_state.dart';
import 'package:invoiceninja_flutter/redux/project/project_state.dart';
@@ -27,6 +29,8 @@ abstract class CompanyState
clientState: ClientState(),
invoiceState: InvoiceState(),
// STARTER: constructor - do not remove comment
+vendorState: VendorState(),
+
taskState: TaskState(),
projectState: ProjectState(),
paymentState: PaymentState(),
@@ -48,6 +52,8 @@ abstract class CompanyState
InvoiceState get invoiceState;
// STARTER: fields - do not remove comment
+VendorState get vendorState;
+
TaskState get taskState;
ProjectState get projectState;
diff --git a/lib/redux/company/company_state.g.dart b/lib/redux/company/company_state.g.dart
index cc215319f..53fc1676d 100644
--- a/lib/redux/company/company_state.g.dart
+++ b/lib/redux/company/company_state.g.dart
@@ -44,6 +44,9 @@ class _$CompanyStateSerializer implements StructuredSerializer {
'invoiceState',
serializers.serialize(object.invoiceState,
specifiedType: const FullType(InvoiceState)),
+ 'vendorState',
+ serializers.serialize(object.vendorState,
+ specifiedType: const FullType(VendorState)),
'taskState',
serializers.serialize(object.taskState,
specifiedType: const FullType(TaskState)),
@@ -98,6 +101,10 @@ class _$CompanyStateSerializer implements StructuredSerializer {
result.invoiceState.replace(serializers.deserialize(value,
specifiedType: const FullType(InvoiceState)) as InvoiceState);
break;
+ case 'vendorState':
+ result.vendorState.replace(serializers.deserialize(value,
+ specifiedType: const FullType(VendorState)) as VendorState);
+ break;
case 'taskState':
result.taskState.replace(serializers.deserialize(value,
specifiedType: const FullType(TaskState)) as TaskState);
@@ -133,6 +140,8 @@ class _$CompanyState extends CompanyState {
@override
final InvoiceState invoiceState;
@override
+ final VendorState vendorState;
+ @override
final TaskState taskState;
@override
final ProjectState projectState;
@@ -150,6 +159,7 @@ class _$CompanyState extends CompanyState {
this.productState,
this.clientState,
this.invoiceState,
+ this.vendorState,
this.taskState,
this.projectState,
this.paymentState,
@@ -167,6 +177,9 @@ class _$CompanyState extends CompanyState {
if (invoiceState == null) {
throw new BuiltValueNullFieldError('CompanyState', 'invoiceState');
}
+ if (vendorState == null) {
+ throw new BuiltValueNullFieldError('CompanyState', 'vendorState');
+ }
if (taskState == null) {
throw new BuiltValueNullFieldError('CompanyState', 'taskState');
}
@@ -197,6 +210,7 @@ class _$CompanyState extends CompanyState {
productState == other.productState &&
clientState == other.clientState &&
invoiceState == other.invoiceState &&
+ vendorState == other.vendorState &&
taskState == other.taskState &&
projectState == other.projectState &&
paymentState == other.paymentState &&
@@ -212,11 +226,13 @@ class _$CompanyState extends CompanyState {
$jc(
$jc(
$jc(
- $jc($jc(0, company.hashCode),
- dashboardState.hashCode),
- productState.hashCode),
- clientState.hashCode),
- invoiceState.hashCode),
+ $jc(
+ $jc($jc(0, company.hashCode),
+ dashboardState.hashCode),
+ productState.hashCode),
+ clientState.hashCode),
+ invoiceState.hashCode),
+ vendorState.hashCode),
taskState.hashCode),
projectState.hashCode),
paymentState.hashCode),
@@ -231,6 +247,7 @@ class _$CompanyState extends CompanyState {
..add('productState', productState)
..add('clientState', clientState)
..add('invoiceState', invoiceState)
+ ..add('vendorState', vendorState)
..add('taskState', taskState)
..add('projectState', projectState)
..add('paymentState', paymentState)
@@ -272,6 +289,12 @@ class CompanyStateBuilder
set invoiceState(InvoiceStateBuilder invoiceState) =>
_$this._invoiceState = invoiceState;
+ VendorStateBuilder _vendorState;
+ VendorStateBuilder get vendorState =>
+ _$this._vendorState ??= new VendorStateBuilder();
+ set vendorState(VendorStateBuilder vendorState) =>
+ _$this._vendorState = vendorState;
+
TaskStateBuilder _taskState;
TaskStateBuilder get taskState =>
_$this._taskState ??= new TaskStateBuilder();
@@ -304,6 +327,7 @@ class CompanyStateBuilder
_productState = _$v.productState?.toBuilder();
_clientState = _$v.clientState?.toBuilder();
_invoiceState = _$v.invoiceState?.toBuilder();
+ _vendorState = _$v.vendorState?.toBuilder();
_taskState = _$v.taskState?.toBuilder();
_projectState = _$v.projectState?.toBuilder();
_paymentState = _$v.paymentState?.toBuilder();
@@ -337,6 +361,7 @@ class CompanyStateBuilder
productState: productState.build(),
clientState: clientState.build(),
invoiceState: invoiceState.build(),
+ vendorState: vendorState.build(),
taskState: taskState.build(),
projectState: projectState.build(),
paymentState: paymentState.build(),
@@ -354,6 +379,8 @@ class CompanyStateBuilder
clientState.build();
_$failedField = 'invoiceState';
invoiceState.build();
+ _$failedField = 'vendorState';
+ vendorState.build();
_$failedField = 'taskState';
taskState.build();
_$failedField = 'projectState';
diff --git a/lib/redux/ui/ui_reducer.dart b/lib/redux/ui/ui_reducer.dart
index e97ef3309..1a459b75a 100644
--- a/lib/redux/ui/ui_reducer.dart
+++ b/lib/redux/ui/ui_reducer.dart
@@ -9,6 +9,8 @@ import 'package:invoiceninja_flutter/redux/invoice/invoice_reducer.dart';
import 'package:redux/redux.dart';
// STARTER: import - do not remove comment
+import 'package:invoiceninja_flutter/redux/vendor/vendor_reducer.dart';
+
import 'package:invoiceninja_flutter/redux/task/task_reducer.dart';
import 'package:invoiceninja_flutter/redux/project/project_reducer.dart';
@@ -34,6 +36,8 @@ UIState uiReducer(UIState state, dynamic action) {
..dashboardUIState
.replace(dashboardUIReducer(state.dashboardUIState, action))
// STARTER: reducer - do not remove comment
+..vendorUIState.replace(vendorUIReducer(state.vendorUIState, action))
+
..taskUIState.replace(taskUIReducer(state.taskUIState, action))
..projectUIState.replace(projectUIReducer(state.projectUIState, action))
..paymentUIState.replace(paymentUIReducer(state.paymentUIState, action))
diff --git a/lib/redux/ui/ui_state.dart b/lib/redux/ui/ui_state.dart
index 5e6084af8..b5a1cdc27 100644
--- a/lib/redux/ui/ui_state.dart
+++ b/lib/redux/ui/ui_state.dart
@@ -8,6 +8,8 @@ import 'package:invoiceninja_flutter/redux/product/product_state.dart';
import 'package:invoiceninja_flutter/ui/auth/login_vm.dart';
// STARTER: import - do not remove comment
+import 'package:invoiceninja_flutter/redux/vendor/vendor_state.dart';
+
import 'package:invoiceninja_flutter/redux/task/task_state.dart';
import 'package:invoiceninja_flutter/redux/project/project_state.dart';
@@ -33,6 +35,8 @@ abstract class UIState implements Built {
clientUIState: ClientUIState(),
invoiceUIState: InvoiceUIState(),
// STARTER: constructor - do not remove comment
+vendorUIState: VendorUIState(),
+
taskUIState: TaskUIState(),
projectUIState: ProjectUIState(),
paymentUIState: PaymentUIState(),
@@ -66,6 +70,8 @@ abstract class UIState implements Built {
String get filter;
// STARTER: properties - do not remove comment
+VendorUIState get vendorUIState;
+
TaskUIState get taskUIState;
ProjectUIState get projectUIState;
diff --git a/lib/redux/ui/ui_state.g.dart b/lib/redux/ui/ui_state.g.dart
index 06c1371af..985f325d7 100644
--- a/lib/redux/ui/ui_state.g.dart
+++ b/lib/redux/ui/ui_state.g.dart
@@ -61,6 +61,9 @@ class _$UIStateSerializer implements StructuredSerializer {
'invoiceUIState',
serializers.serialize(object.invoiceUIState,
specifiedType: const FullType(InvoiceUIState)),
+ 'vendorUIState',
+ serializers.serialize(object.vendorUIState,
+ specifiedType: const FullType(VendorUIState)),
'taskUIState',
serializers.serialize(object.taskUIState,
specifiedType: const FullType(TaskUIState)),
@@ -140,6 +143,10 @@ class _$UIStateSerializer implements StructuredSerializer {
result.filter = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
+ case 'vendorUIState':
+ result.vendorUIState.replace(serializers.deserialize(value,
+ specifiedType: const FullType(VendorUIState)) as VendorUIState);
+ break;
case 'taskUIState':
result.taskUIState.replace(serializers.deserialize(value,
specifiedType: const FullType(TaskUIState)) as TaskUIState);
@@ -187,6 +194,8 @@ class _$UIState extends UIState {
@override
final String filter;
@override
+ final VendorUIState vendorUIState;
+ @override
final TaskUIState taskUIState;
@override
final ProjectUIState projectUIState;
@@ -210,6 +219,7 @@ class _$UIState extends UIState {
this.clientUIState,
this.invoiceUIState,
this.filter,
+ this.vendorUIState,
this.taskUIState,
this.projectUIState,
this.paymentUIState,
@@ -245,6 +255,9 @@ class _$UIState extends UIState {
if (invoiceUIState == null) {
throw new BuiltValueNullFieldError('UIState', 'invoiceUIState');
}
+ if (vendorUIState == null) {
+ throw new BuiltValueNullFieldError('UIState', 'vendorUIState');
+ }
if (taskUIState == null) {
throw new BuiltValueNullFieldError('UIState', 'taskUIState');
}
@@ -281,6 +294,7 @@ class _$UIState extends UIState {
clientUIState == other.clientUIState &&
invoiceUIState == other.invoiceUIState &&
filter == other.filter &&
+ vendorUIState == other.vendorUIState &&
taskUIState == other.taskUIState &&
projectUIState == other.projectUIState &&
paymentUIState == other.paymentUIState &&
@@ -304,22 +318,24 @@ class _$UIState extends UIState {
$jc(
$jc(
$jc(
- 0,
- selectedCompanyIndex
+ $jc(
+ 0,
+ selectedCompanyIndex
+ .hashCode),
+ currentRoute
.hashCode),
- currentRoute
+ enableDarkMode
.hashCode),
- enableDarkMode
+ requireAuthentication
.hashCode),
- requireAuthentication
- .hashCode),
- emailPayment.hashCode),
- autoStartTasks.hashCode),
- dashboardUIState.hashCode),
- productUIState.hashCode),
- clientUIState.hashCode),
- invoiceUIState.hashCode),
- filter.hashCode),
+ emailPayment.hashCode),
+ autoStartTasks.hashCode),
+ dashboardUIState.hashCode),
+ productUIState.hashCode),
+ clientUIState.hashCode),
+ invoiceUIState.hashCode),
+ filter.hashCode),
+ vendorUIState.hashCode),
taskUIState.hashCode),
projectUIState.hashCode),
paymentUIState.hashCode),
@@ -340,6 +356,7 @@ class _$UIState extends UIState {
..add('clientUIState', clientUIState)
..add('invoiceUIState', invoiceUIState)
..add('filter', filter)
+ ..add('vendorUIState', vendorUIState)
..add('taskUIState', taskUIState)
..add('projectUIState', projectUIState)
..add('paymentUIState', paymentUIState)
@@ -407,6 +424,12 @@ class UIStateBuilder implements Builder {
String get filter => _$this._filter;
set filter(String filter) => _$this._filter = filter;
+ VendorUIStateBuilder _vendorUIState;
+ VendorUIStateBuilder get vendorUIState =>
+ _$this._vendorUIState ??= new VendorUIStateBuilder();
+ set vendorUIState(VendorUIStateBuilder vendorUIState) =>
+ _$this._vendorUIState = vendorUIState;
+
TaskUIStateBuilder _taskUIState;
TaskUIStateBuilder get taskUIState =>
_$this._taskUIState ??= new TaskUIStateBuilder();
@@ -446,6 +469,7 @@ class UIStateBuilder implements Builder {
_clientUIState = _$v.clientUIState?.toBuilder();
_invoiceUIState = _$v.invoiceUIState?.toBuilder();
_filter = _$v.filter;
+ _vendorUIState = _$v.vendorUIState?.toBuilder();
_taskUIState = _$v.taskUIState?.toBuilder();
_projectUIState = _$v.projectUIState?.toBuilder();
_paymentUIState = _$v.paymentUIState?.toBuilder();
@@ -485,6 +509,7 @@ class UIStateBuilder implements Builder {
clientUIState: clientUIState.build(),
invoiceUIState: invoiceUIState.build(),
filter: filter,
+ vendorUIState: vendorUIState.build(),
taskUIState: taskUIState.build(),
projectUIState: projectUIState.build(),
paymentUIState: paymentUIState.build(),
@@ -501,6 +526,8 @@ class UIStateBuilder implements Builder {
_$failedField = 'invoiceUIState';
invoiceUIState.build();
+ _$failedField = 'vendorUIState';
+ vendorUIState.build();
_$failedField = 'taskUIState';
taskUIState.build();
_$failedField = 'projectUIState';
diff --git a/lib/redux/vendor/vendor_actions.dart b/lib/redux/vendor/vendor_actions.dart
new file mode 100644
index 000000000..b49d498d6
--- /dev/null
+++ b/lib/redux/vendor/vendor_actions.dart
@@ -0,0 +1,226 @@
+import 'dart:async';
+import 'package:flutter/widgets.dart';
+import 'package:built_collection/built_collection.dart';
+import 'package:invoiceninja_flutter/data/models/models.dart';
+import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
+
+class ViewVendorList implements PersistUI {
+ ViewVendorList(this.context);
+
+ final BuildContext context;
+}
+
+class ViewVendor implements PersistUI {
+ ViewVendor({this.vendorId, this.context});
+
+ final int vendorId;
+ final BuildContext context;
+}
+
+class EditVendor implements PersistUI {
+ EditVendor({this.vendor, this.context, this.completer, this.trackRoute = true});
+
+ final VendorEntity vendor;
+ final BuildContext context;
+ final Completer completer;
+ final bool trackRoute;
+}
+
+class UpdateVendor implements PersistUI {
+ UpdateVendor(this.vendor);
+
+ final VendorEntity vendor;
+}
+
+class LoadVendor {
+ LoadVendor({this.completer, this.vendorId, this.loadActivities = false});
+
+ final Completer completer;
+ final int vendorId;
+ final bool loadActivities;
+}
+
+class LoadVendorActivity {
+ LoadVendorActivity({this.completer, this.vendorId});
+
+ final Completer completer;
+ final int vendorId;
+}
+
+class LoadVendors {
+ LoadVendors({this.completer, this.force = false});
+
+ final Completer completer;
+ final bool force;
+}
+
+class LoadVendorRequest implements StartLoading {}
+
+class LoadVendorFailure implements StopLoading {
+ LoadVendorFailure(this.error);
+
+ final dynamic error;
+
+ @override
+ String toString() {
+ return 'LoadVendorFailure{error: $error}';
+ }
+}
+
+class LoadVendorSuccess implements StopLoading, PersistData {
+ LoadVendorSuccess(this.vendor);
+
+ final VendorEntity vendor;
+
+ @override
+ String toString() {
+ return 'LoadVendorSuccess{vendor: $vendor}';
+ }
+}
+
+class LoadVendorsRequest implements StartLoading {}
+
+class LoadVendorsFailure implements StopLoading {
+ LoadVendorsFailure(this.error);
+
+ final dynamic error;
+
+ @override
+ String toString() {
+ return 'LoadVendorsFailure{error: $error}';
+ }
+}
+
+class LoadVendorsSuccess implements StopLoading, PersistData {
+ LoadVendorsSuccess(this.vendors);
+
+ final BuiltList vendors;
+
+ @override
+ String toString() {
+ return 'LoadVendorsSuccess{vendors: $vendors}';
+ }
+}
+
+
+class SaveVendorRequest implements StartSaving {
+ SaveVendorRequest({this.completer, this.vendor});
+
+ final Completer completer;
+ final VendorEntity vendor;
+}
+
+class SaveVendorSuccess implements StopSaving, PersistData, PersistUI {
+ SaveVendorSuccess(this.vendor);
+
+ final VendorEntity vendor;
+}
+
+class AddVendorSuccess implements StopSaving, PersistData, PersistUI {
+ AddVendorSuccess(this.vendor);
+
+ final VendorEntity vendor;
+}
+
+class SaveVendorFailure implements StopSaving {
+ SaveVendorFailure (this.error);
+
+ final Object error;
+}
+
+class ArchiveVendorRequest implements StartSaving {
+ ArchiveVendorRequest(this.completer, this.vendorId);
+
+ final Completer completer;
+ final int vendorId;
+}
+
+class ArchiveVendorSuccess implements StopSaving, PersistData {
+ ArchiveVendorSuccess(this.vendor);
+
+ final VendorEntity vendor;
+}
+
+class ArchiveVendorFailure implements StopSaving {
+ ArchiveVendorFailure(this.vendor);
+
+ final VendorEntity vendor;
+}
+
+class DeleteVendorRequest implements StartSaving {
+ DeleteVendorRequest(this.completer, this.vendorId);
+
+ final Completer completer;
+ final int vendorId;
+}
+
+class DeleteVendorSuccess implements StopSaving, PersistData {
+ DeleteVendorSuccess(this.vendor);
+
+ final VendorEntity vendor;
+}
+
+class DeleteVendorFailure implements StopSaving {
+ DeleteVendorFailure(this.vendor);
+
+ final VendorEntity vendor;
+}
+
+class RestoreVendorRequest implements StartSaving {
+ RestoreVendorRequest(this.completer, this.vendorId);
+
+ final Completer completer;
+ final int vendorId;
+}
+
+class RestoreVendorSuccess implements StopSaving, PersistData {
+ RestoreVendorSuccess(this.vendor);
+
+ final VendorEntity vendor;
+}
+
+class RestoreVendorFailure implements StopSaving {
+ RestoreVendorFailure(this.vendor);
+
+ final VendorEntity vendor;
+}
+
+
+
+
+class FilterVendors {
+ FilterVendors(this.filter);
+
+ final String filter;
+}
+
+class SortVendors implements PersistUI {
+ SortVendors(this.field);
+
+ final String field;
+}
+
+class FilterVendorsByState implements PersistUI {
+ FilterVendorsByState(this.state);
+
+ final EntityState state;
+}
+
+class FilterVendorsByCustom1 implements PersistUI {
+ FilterVendorsByCustom1(this.value);
+
+ final String value;
+}
+
+class FilterVendorsByCustom2 implements PersistUI {
+ FilterVendorsByCustom2(this.value);
+
+ final String value;
+}
+
+class FilterVendorsByEntity implements PersistUI {
+ FilterVendorsByEntity({this.entityId, this.entityType});
+
+ final int entityId;
+ final EntityType entityType;
+}
diff --git a/lib/redux/vendor/vendor_middleware.dart b/lib/redux/vendor/vendor_middleware.dart
new file mode 100644
index 000000000..e71453525
--- /dev/null
+++ b/lib/redux/vendor/vendor_middleware.dart
@@ -0,0 +1,233 @@
+import 'package:flutter/material.dart';
+import 'package:flutter/widgets.dart';
+import 'package:redux/redux.dart';
+import 'package:invoiceninja_flutter/data/models/models.dart';
+import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
+import 'package:invoiceninja_flutter/ui/vendor/vendor_screen.dart';
+import 'package:invoiceninja_flutter/ui/vendor/edit/vendor_edit_vm.dart';
+import 'package:invoiceninja_flutter/ui/vendor/view/vendor_view_vm.dart';
+import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart';
+import 'package:invoiceninja_flutter/redux/app/app_state.dart';
+import 'package:invoiceninja_flutter/data/repositories/vendor_repository.dart';
+
+List> createStoreVendorsMiddleware([
+ VendorRepository repository = const VendorRepository(),
+]) {
+ final viewVendorList = _viewVendorList();
+ final viewVendor = _viewVendor();
+ final editVendor = _editVendor();
+ final loadVendors = _loadVendors(repository);
+ final loadVendor = _loadVendor(repository);
+ final saveVendor = _saveVendor(repository);
+ final archiveVendor = _archiveVendor(repository);
+ final deleteVendor = _deleteVendor(repository);
+ final restoreVendor = _restoreVendor(repository);
+
+ return [
+ TypedMiddleware(viewVendorList),
+ TypedMiddleware(viewVendor),
+ TypedMiddleware(editVendor),
+ TypedMiddleware(loadVendors),
+ TypedMiddleware(loadVendor),
+ TypedMiddleware(saveVendor),
+ TypedMiddleware(archiveVendor),
+ TypedMiddleware(deleteVendor),
+ TypedMiddleware(restoreVendor),
+ ];
+}
+
+Middleware _editVendor() {
+ return (Store store, dynamic action, NextDispatcher next) async {
+ next(action);
+
+ store.dispatch(UpdateCurrentRoute(VendorEditScreen.route));
+ final vendor =
+ await Navigator.of(action.context).pushNamed(VendorEditScreen.route);
+
+ if (action.completer != null && vendor != null) {
+ action.completer.complete(vendor);
+ }
+ };
+}
+
+Middleware _viewVendor() {
+ return (Store store, dynamic action, NextDispatcher next) async {
+ next(action);
+
+ store.dispatch(UpdateCurrentRoute(VendorViewScreen.route));
+ Navigator.of(action.context).pushNamed(VendorViewScreen.route);
+ };
+}
+
+Middleware _viewVendorList() {
+ return (Store store, dynamic action, NextDispatcher next) {
+ next(action);
+
+ store.dispatch(UpdateCurrentRoute(VendorScreen.route));
+
+ Navigator.of(action.context).pushNamedAndRemoveUntil(VendorScreen.route, (Route route) => false);
+ };
+}
+
+Middleware _archiveVendor(VendorRepository repository) {
+ return (Store store, dynamic action, NextDispatcher next) {
+ final origVendor = store.state.vendorState.map[action.vendorId];
+ repository
+ .saveData(store.state.selectedCompany, store.state.authState,
+ origVendor, EntityAction.archive)
+ .then((VendorEntity vendor) {
+ store.dispatch(ArchiveVendorSuccess(vendor));
+ if (action.completer != null) {
+ action.completer.complete(null);
+ }
+ }).catchError((Object error) {
+ print(error);
+ store.dispatch(ArchiveVendorFailure(origVendor));
+ if (action.completer != null) {
+ action.completer.completeError(error);
+ }
+ });
+
+ next(action);
+ };
+}
+
+Middleware _deleteVendor(VendorRepository repository) {
+ return (Store store, dynamic action, NextDispatcher next) {
+ final origVendor = store.state.vendorState.map[action.vendorId];
+ repository
+ .saveData(store.state.selectedCompany, store.state.authState,
+ origVendor, EntityAction.delete)
+ .then((VendorEntity vendor) {
+ store.dispatch(DeleteVendorSuccess(vendor));
+ if (action.completer != null) {
+ action.completer.complete(null);
+ }
+ }).catchError((Object error) {
+ print(error);
+ store.dispatch(DeleteVendorFailure(origVendor));
+ if (action.completer != null) {
+ action.completer.completeError(error);
+ }
+ });
+
+ next(action);
+ };
+}
+
+Middleware _restoreVendor(VendorRepository repository) {
+ return (Store store, dynamic action, NextDispatcher next) {
+ final origVendor = store.state.vendorState.map[action.vendorId];
+ repository
+ .saveData(store.state.selectedCompany, store.state.authState,
+ origVendor, EntityAction.restore)
+ .then((VendorEntity vendor) {
+ store.dispatch(RestoreVendorSuccess(vendor));
+ if (action.completer != null) {
+ action.completer.complete(null);
+ }
+ }).catchError((Object error) {
+ print(error);
+ store.dispatch(RestoreVendorFailure(origVendor));
+ if (action.completer != null) {
+ action.completer.completeError(error);
+ }
+ });
+
+ next(action);
+ };
+}
+
+Middleware _saveVendor(VendorRepository repository) {
+ return (Store store, dynamic action, NextDispatcher next) {
+ repository
+ .saveData(
+ store.state.selectedCompany, store.state.authState, action.vendor)
+ .then((VendorEntity vendor) {
+ if (action.vendor.isNew) {
+ store.dispatch(AddVendorSuccess(vendor));
+ } else {
+ store.dispatch(SaveVendorSuccess(vendor));
+ }
+ action.completer.complete(vendor);
+ }).catchError((Object error) {
+ print(error);
+ store.dispatch(SaveVendorFailure(error));
+ action.completer.completeError(error);
+ });
+
+ next(action);
+ };
+}
+
+Middleware _loadVendor(VendorRepository repository) {
+ return (Store store, dynamic action, NextDispatcher next) {
+ final AppState state = store.state;
+
+ if (state.isLoading) {
+ next(action);
+ return;
+ }
+
+ store.dispatch(LoadVendorRequest());
+ repository
+ .loadItem(state.selectedCompany, state.authState, action.vendorId)
+ .then((vendor) {
+ store.dispatch(LoadVendorSuccess(vendor));
+
+ if (action.completer != null) {
+ action.completer.complete(null);
+ }
+ }).catchError((Object error) {
+ print(error);
+ store.dispatch(LoadVendorFailure(error));
+ if (action.completer != null) {
+ action.completer.completeError(error);
+ }
+ });
+
+ next(action);
+ };
+}
+
+Middleware _loadVendors(VendorRepository repository) {
+ return (Store store, dynamic action, NextDispatcher next) {
+ final AppState state = store.state;
+
+ if (!state.vendorState.isStale && !action.force) {
+ next(action);
+ return;
+ }
+
+ if (state.isLoading) {
+ next(action);
+ return;
+ }
+
+ final int updatedAt = (state.vendorState.lastUpdated / 1000).round();
+
+ store.dispatch(LoadVendorsRequest());
+ repository
+ .loadList(state.selectedCompany, state.authState, updatedAt)
+ .then((data) {
+ store.dispatch(LoadVendorsSuccess(data));
+
+ if (action.completer != null) {
+ action.completer.complete(null);
+ }
+ /*
+ if (state.productState.isStale) {
+ store.dispatch(LoadProducts());
+ }
+ */
+ }).catchError((Object error) {
+ print(error);
+ store.dispatch(LoadVendorsFailure(error));
+ if (action.completer != null) {
+ action.completer.completeError(error);
+ }
+ });
+
+ next(action);
+ };
+}
diff --git a/lib/redux/vendor/vendor_reducer.dart b/lib/redux/vendor/vendor_reducer.dart
new file mode 100644
index 000000000..b856d5411
--- /dev/null
+++ b/lib/redux/vendor/vendor_reducer.dart
@@ -0,0 +1,197 @@
+import 'package:redux/redux.dart';
+import 'package:invoiceninja_flutter/data/models/models.dart';
+import 'package:invoiceninja_flutter/redux/company/company_actions.dart';
+import 'package:invoiceninja_flutter/redux/ui/entity_ui_state.dart';
+import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
+import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart';
+import 'package:invoiceninja_flutter/redux/vendor/vendor_state.dart';
+
+EntityUIState vendorUIReducer(VendorUIState state, dynamic action) {
+ return state.rebuild((b) => b
+ ..listUIState.replace(vendorListReducer(state.listUIState, action))
+ ..editing.replace(editingReducer(state.editing, action))
+ ..selectedId = selectedIdReducer(state.selectedId, action));
+}
+
+Reducer selectedIdReducer = combineReducers([
+ TypedReducer(
+ (int selectedId, dynamic action) => action.vendorId),
+ TypedReducer(
+ (int selectedId, dynamic action) => action.vendor.id),
+]);
+
+final editingReducer = combineReducers([
+ TypedReducer(_updateEditing),
+ TypedReducer(_updateEditing),
+ TypedReducer(_updateEditing),
+ TypedReducer(_updateEditing),
+ TypedReducer(_updateEditing),
+ TypedReducer(_updateEditing),
+ TypedReducer(_updateEditing),
+ TypedReducer(_clearEditing),
+]);
+
+VendorEntity _clearEditing(VendorEntity vendor, dynamic action) {
+ return VendorEntity();
+}
+
+VendorEntity _updateEditing(VendorEntity vendor, dynamic action) {
+ return action.vendor;
+}
+
+
+final vendorListReducer = combineReducers([
+ TypedReducer(_sortVendors),
+ TypedReducer(_filterVendorsByState),
+ TypedReducer(_filterVendors),
+ TypedReducer(_filterVendorsByCustom1),
+ TypedReducer(_filterVendorsByCustom2),
+ TypedReducer(_filterVendorsByClient),
+]);
+
+ListUIState _filterVendorsByClient(
+ ListUIState vendorListState, FilterVendorsByEntity action) {
+ return vendorListState.rebuild((b) => b
+ ..filterEntityId = action.entityId
+ ..filterEntityType = action.entityType);
+}
+
+ListUIState _filterVendorsByCustom1(
+ ListUIState vendorListState, FilterVendorsByCustom1 action) {
+ if (vendorListState.custom1Filters.contains(action.value)) {
+ return vendorListState
+ .rebuild((b) => b..custom1Filters.remove(action.value));
+ } else {
+ return vendorListState.rebuild((b) => b..custom1Filters.add(action.value));
+ }
+}
+
+ListUIState _filterVendorsByCustom2(
+ ListUIState vendorListState, FilterVendorsByCustom2 action) {
+ if (vendorListState.custom2Filters.contains(action.value)) {
+ return vendorListState
+ .rebuild((b) => b..custom2Filters.remove(action.value));
+ } else {
+ return vendorListState.rebuild((b) => b..custom2Filters.add(action.value));
+ }
+}
+
+ListUIState _filterVendorsByState(
+ ListUIState vendorListState, FilterVendorsByState action) {
+ if (vendorListState.stateFilters.contains(action.state)) {
+ return vendorListState.rebuild((b) => b..stateFilters.remove(action.state));
+ } else {
+ return vendorListState.rebuild((b) => b..stateFilters.add(action.state));
+ }
+}
+
+ListUIState _filterVendors(ListUIState vendorListState, FilterVendors action) {
+ return vendorListState.rebuild((b) => b..filter = action.filter);
+}
+
+ListUIState _sortVendors(ListUIState vendorListState, SortVendors action) {
+ return vendorListState.rebuild((b) => b
+ ..sortAscending = b.sortField != action.field || !b.sortAscending
+ ..sortField = action.field);
+}
+
+final vendorsReducer = combineReducers([
+ TypedReducer(_updateVendor),
+ TypedReducer(_addVendor),
+ TypedReducer(_setLoadedVendors),
+ TypedReducer(_setLoadedVendor),
+ TypedReducer(_archiveVendorRequest),
+ TypedReducer(_archiveVendorSuccess),
+ TypedReducer(_archiveVendorFailure),
+ TypedReducer(_deleteVendorRequest),
+ TypedReducer(_deleteVendorSuccess),
+ TypedReducer(_deleteVendorFailure),
+ TypedReducer(_restoreVendorRequest),
+ TypedReducer(_restoreVendorSuccess),
+ TypedReducer(_restoreVendorFailure),
+]);
+
+VendorState _archiveVendorRequest(
+ VendorState vendorState, ArchiveVendorRequest action) {
+ final vendor = vendorState.map[action.vendorId]
+ .rebuild((b) => b..archivedAt = DateTime.now().millisecondsSinceEpoch);
+
+ return vendorState.rebuild((b) => b..map[action.vendorId] = vendor);
+}
+
+VendorState _archiveVendorSuccess(
+ VendorState vendorState, ArchiveVendorSuccess action) {
+ return vendorState.rebuild((b) => b..map[action.vendor.id] = action.vendor);
+}
+
+VendorState _archiveVendorFailure(
+ VendorState vendorState, ArchiveVendorFailure action) {
+ return vendorState.rebuild((b) => b..map[action.vendor.id] = action.vendor);
+}
+
+VendorState _deleteVendorRequest(
+ VendorState vendorState, DeleteVendorRequest action) {
+ final vendor = vendorState.map[action.vendorId].rebuild((b) => b
+ ..archivedAt = DateTime.now().millisecondsSinceEpoch
+ ..isDeleted = true);
+
+ return vendorState.rebuild((b) => b..map[action.vendorId] = vendor);
+}
+
+VendorState _deleteVendorSuccess(
+ VendorState vendorState, DeleteVendorSuccess action) {
+ return vendorState.rebuild((b) => b..map[action.vendor.id] = action.vendor);
+}
+
+VendorState _deleteVendorFailure(
+ VendorState vendorState, DeleteVendorFailure action) {
+ return vendorState.rebuild((b) => b..map[action.vendor.id] = action.vendor);
+}
+
+VendorState _restoreVendorRequest(
+ VendorState vendorState, RestoreVendorRequest action) {
+ final vendor = vendorState.map[action.vendorId].rebuild((b) => b
+ ..archivedAt = null
+ ..isDeleted = false);
+ return vendorState.rebuild((b) => b..map[action.vendorId] = vendor);
+}
+
+VendorState _restoreVendorSuccess(
+ VendorState vendorState, RestoreVendorSuccess action) {
+ return vendorState.rebuild((b) => b..map[action.vendor.id] = action.vendor);
+}
+
+VendorState _restoreVendorFailure(
+ VendorState vendorState, RestoreVendorFailure action) {
+ return vendorState.rebuild((b) => b..map[action.vendor.id] = action.vendor);
+}
+
+VendorState _addVendor(VendorState vendorState, AddVendorSuccess action) {
+ return vendorState.rebuild((b) => b
+ ..map[action.vendor.id] = action.vendor
+ ..list.add(action.vendor.id));
+}
+
+VendorState _updateVendor(VendorState vendorState, SaveVendorSuccess action) {
+ return vendorState.rebuild((b) => b
+ ..map[action.vendor.id] = action.vendor);
+}
+
+VendorState _setLoadedVendor(
+ VendorState vendorState, LoadVendorSuccess action) {
+ return vendorState.rebuild((b) => b
+ ..map[action.vendor.id] = action.vendor);
+}
+
+VendorState _setLoadedVendors(
+ VendorState vendorState, LoadVendorsSuccess action) {
+ final state = vendorState.rebuild((b) => b
+ ..lastUpdated = DateTime.now().millisecondsSinceEpoch
+ ..map.addAll(Map.fromIterable(
+ action.vendors,
+ key: (dynamic item) => item.id,
+ value: (dynamic item) => item,
+ )));
+
+ return state.rebuild((b) => b..list.replace(state.map.keys));
+}
diff --git a/lib/redux/vendor/vendor_selectors.dart b/lib/redux/vendor/vendor_selectors.dart
new file mode 100644
index 000000000..271313390
--- /dev/null
+++ b/lib/redux/vendor/vendor_selectors.dart
@@ -0,0 +1,74 @@
+import 'package:memoize/memoize.dart';
+import 'package:built_collection/built_collection.dart';
+import 'package:invoiceninja_flutter/data/models/models.dart';
+import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
+
+var memoizedDropdownVendorList = memo3(
+ (BuiltMap vendorMap, BuiltList vendorList,
+ int clientId) =>
+ dropdownVendorsSelector(vendorMap, vendorList, clientId));
+
+List dropdownVendorsSelector(BuiltMap vendorMap,
+ BuiltList vendorList, int clientId) {
+ final list = vendorList.where((vendorId) {
+ final vendor = vendorMap[vendorId];
+ /*
+ if (clientId != null && clientId > 0 && vendor.clientId != clientId) {
+ return false;
+ }
+ */
+ return vendor.isActive;
+ }).toList();
+
+ list.sort((vendorAId, vendorBId) {
+ final vendorA = vendorMap[vendorAId];
+ final vendorB = vendorMap[vendorBId];
+ return vendorA.compareTo(vendorB, VendorFields.name, true);
+ });
+
+ return list;
+}
+
+var memoizedFilteredVendorList = memo3((BuiltMap vendorMap,
+ BuiltList vendorList, ListUIState vendorListState) =>
+ filteredVendorsSelector(vendorMap, vendorList, vendorListState));
+
+List filteredVendorsSelector(BuiltMap vendorMap,
+ BuiltList vendorList, ListUIState vendorListState) {
+ final list = vendorList.where((vendorId) {
+ final vendor = vendorMap[vendorId];
+ if (!vendor.matchesStates(vendorListState.stateFilters)) {
+ return false;
+ }
+ /*
+ if (vendorListState.filterEntityId != null &&
+ vendor.clientId != vendorListState.filterEntityId) {
+ return false;
+ }
+ */
+ if (vendorListState.custom1Filters.isNotEmpty &&
+ !vendorListState.custom1Filters.contains(vendor.customValue1)) {
+ return false;
+ }
+ if (vendorListState.custom2Filters.isNotEmpty &&
+ !vendorListState.custom2Filters.contains(vendor.customValue2)) {
+ return false;
+ }
+ /*
+ if (vendorListState.filterEntityId != null &&
+ vendor.entityId != vendorListState.filterEntityId) {
+ return false;
+ }
+ */
+ return vendor.matchesFilter(vendorListState.filter);
+ }).toList();
+
+ list.sort((vendorAId, vendorBId) {
+ final vendorA = vendorMap[vendorAId];
+ final vendorB = vendorMap[vendorBId];
+ return vendorA.compareTo(
+ vendorB, vendorListState.sortField, vendorListState.sortAscending);
+ });
+
+ return list;
+}
diff --git a/lib/redux/vendor/vendor_state.dart b/lib/redux/vendor/vendor_state.dart
new file mode 100644
index 000000000..1158d0d95
--- /dev/null
+++ b/lib/redux/vendor/vendor_state.dart
@@ -0,0 +1,59 @@
+import 'package:built_value/built_value.dart';
+import 'package:built_value/serializer.dart';
+import 'package:built_collection/built_collection.dart';
+import 'package:invoiceninja_flutter/constants.dart';
+import 'package:invoiceninja_flutter/data/models/vendor_model.dart';
+import 'package:invoiceninja_flutter/redux/ui/entity_ui_state.dart';
+import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
+
+part 'vendor_state.g.dart';
+
+abstract class VendorState implements Built {
+
+ factory VendorState() {
+ return _$VendorState._(
+ lastUpdated: 0,
+ map: BuiltMap(),
+ list: BuiltList(),
+ );
+ }
+ VendorState._();
+
+ @nullable
+ int get lastUpdated;
+
+ BuiltMap get map;
+ BuiltList get list;
+
+ bool get isStale {
+ if (! isLoaded) {
+ return true;
+ }
+
+ return DateTime.now().millisecondsSinceEpoch - lastUpdated > kMillisecondsToRefreshData;
+ }
+
+ bool get isLoaded => lastUpdated != null && lastUpdated > 0;
+
+ static Serializer get serializer => _$vendorStateSerializer;
+}
+
+abstract class VendorUIState extends Object with EntityUIState implements Built {
+
+ factory VendorUIState() {
+ return _$VendorUIState._(
+ listUIState: ListUIState(VendorFields.name),
+ editing: VendorEntity(),
+ selectedId: 0,
+ );
+ }
+ VendorUIState._();
+
+ @nullable
+ VendorEntity get editing;
+
+ @override
+ bool get isCreatingNew => editing.isNew;
+
+ static Serializer get serializer => _$vendorUIStateSerializer;
+}
\ No newline at end of file
diff --git a/lib/redux/vendor/vendor_state.g.dart b/lib/redux/vendor/vendor_state.g.dart
new file mode 100644
index 000000000..52337c5f9
--- /dev/null
+++ b/lib/redux/vendor/vendor_state.g.dart
@@ -0,0 +1,388 @@
+// GENERATED CODE - DO NOT MODIFY BY HAND
+
+part of 'vendor_state.dart';
+
+// **************************************************************************
+// BuiltValueGenerator
+// **************************************************************************
+
+// ignore_for_file: always_put_control_body_on_new_line
+// ignore_for_file: annotate_overrides
+// ignore_for_file: avoid_annotating_with_dynamic
+// ignore_for_file: avoid_catches_without_on_clauses
+// ignore_for_file: avoid_returning_this
+// ignore_for_file: lines_longer_than_80_chars
+// ignore_for_file: omit_local_variable_types
+// ignore_for_file: prefer_expression_function_bodies
+// ignore_for_file: sort_constructors_first
+// ignore_for_file: unnecessary_const
+// ignore_for_file: unnecessary_new
+// ignore_for_file: test_types_in_equals
+
+Serializer _$vendorStateSerializer = new _$VendorStateSerializer();
+Serializer _$vendorUIStateSerializer =
+ new _$VendorUIStateSerializer();
+
+class _$VendorStateSerializer implements StructuredSerializer {
+ @override
+ final Iterable types = const [VendorState, _$VendorState];
+ @override
+ final String wireName = 'VendorState';
+
+ @override
+ Iterable serialize(Serializers serializers, VendorState object,
+ {FullType specifiedType = FullType.unspecified}) {
+ final result =