This commit is contained in:
Hillel Coren 2020-03-02 19:48:22 +02:00
parent 8d42b7df68
commit 70f8ace04b
39 changed files with 2743 additions and 720 deletions

View File

@ -14,7 +14,7 @@ abstract class CreditListResponse
CreditListResponse._();
BuiltList<CreditEntity> get data;
BuiltList<InvoiceEntity> get data;
static Serializer<CreditListResponse> get serializer =>
_$creditListResponseSerializer;
@ -27,7 +27,7 @@ abstract class CreditItemResponse
CreditItemResponse._();
CreditEntity get data;
InvoiceEntity get data;
static Serializer<CreditItemResponse> get serializer =>
_$creditItemResponseSerializer;
@ -46,110 +46,3 @@ class CreditFields {
static const String archivedAt = 'archivedAt';
static const String isDeleted = 'isDeleted';
}
abstract class CreditEntity extends Object
with BaseEntity, SelectableEntity
implements Built<CreditEntity, CreditEntityBuilder> {
factory CreditEntity() {
return _$CreditEntity._(
id: BaseEntity.nextId,
isChanged: false,
amount: 0.0,
balance: 0.0,
creditDate: '',
creditNumber: '',
privateNotes: '',
publicNotes: '',
clientId: 0,
updatedAt: 0,
archivedAt: 0,
isDeleted: false,
);
}
CreditEntity._();
CreditEntity get clone => rebuild((b) => b
..id = BaseEntity.nextId
..isChanged = false
..isDeleted = false);
@override
EntityType get entityType {
return EntityType.credit;
}
double get amount;
double get balance;
@BuiltValueField(wireName: 'credit_date')
String get creditDate;
@BuiltValueField(wireName: 'credit_number')
String get creditNumber;
@BuiltValueField(wireName: 'private_notes')
String get privateNotes;
@BuiltValueField(wireName: 'public_notes')
String get publicNotes;
@BuiltValueField(wireName: 'client_id')
int get clientId;
@override
List<EntityAction> getActions(
{UserCompanyEntity userCompany,
ClientEntity client,
bool includeEdit = false,
bool multiselect = false}) {
final actions = <EntityAction>[];
return actions..addAll(super.getActions(userCompany: userCompany));
}
int compareTo(CreditEntity credit, String sortField, bool sortAscending) {
int response = 0;
final CreditEntity creditA = sortAscending ? this : credit;
final CreditEntity creditB = sortAscending ? credit : this;
switch (sortField) {
case CreditFields.amount:
response = creditA.amount.compareTo(creditB.amount);
}
return response;
}
@override
bool matchesFilter(String filter) {
if (filter == null || filter.isEmpty) {
return true;
}
return publicNotes.toLowerCase().contains(filter);
}
@override
String matchesFilterValue(String filter) {
if (filter == null || filter.isEmpty) {
return null;
}
return null;
}
@override
String get listDisplayName {
return publicNotes;
}
@override
double get listDisplayAmount => null;
@override
FormatNumberType get listDisplayAmountType => FormatNumberType.money;
static Serializer<CreditEntity> get serializer => _$creditEntitySerializer;
}

View File

@ -10,8 +10,6 @@ Serializer<CreditListResponse> _$creditListResponseSerializer =
new _$CreditListResponseSerializer();
Serializer<CreditItemResponse> _$creditItemResponseSerializer =
new _$CreditItemResponseSerializer();
Serializer<CreditEntity> _$creditEntitySerializer =
new _$CreditEntitySerializer();
class _$CreditListResponseSerializer
implements StructuredSerializer<CreditListResponse> {
@ -27,7 +25,7 @@ class _$CreditListResponseSerializer
'data',
serializers.serialize(object.data,
specifiedType:
const FullType(BuiltList, const [const FullType(CreditEntity)])),
const FullType(BuiltList, const [const FullType(InvoiceEntity)])),
];
return result;
@ -48,7 +46,7 @@ class _$CreditListResponseSerializer
case 'data':
result.data.replace(serializers.deserialize(value,
specifiedType: const FullType(
BuiltList, const [const FullType(CreditEntity)]))
BuiltList, const [const FullType(InvoiceEntity)]))
as BuiltList<Object>);
break;
}
@ -71,7 +69,7 @@ class _$CreditItemResponseSerializer
final result = <Object>[
'data',
serializers.serialize(object.data,
specifiedType: const FullType(CreditEntity)),
specifiedType: const FullType(InvoiceEntity)),
];
return result;
@ -91,178 +89,7 @@ class _$CreditItemResponseSerializer
switch (key) {
case 'data':
result.data.replace(serializers.deserialize(value,
specifiedType: const FullType(CreditEntity)) as CreditEntity);
break;
}
}
return result.build();
}
}
class _$CreditEntitySerializer implements StructuredSerializer<CreditEntity> {
@override
final Iterable<Type> types = const [CreditEntity, _$CreditEntity];
@override
final String wireName = 'CreditEntity';
@override
Iterable<Object> serialize(Serializers serializers, CreditEntity object,
{FullType specifiedType = FullType.unspecified}) {
final result = <Object>[
'amount',
serializers.serialize(object.amount,
specifiedType: const FullType(double)),
'balance',
serializers.serialize(object.balance,
specifiedType: const FullType(double)),
'credit_date',
serializers.serialize(object.creditDate,
specifiedType: const FullType(String)),
'credit_number',
serializers.serialize(object.creditNumber,
specifiedType: const FullType(String)),
'private_notes',
serializers.serialize(object.privateNotes,
specifiedType: const FullType(String)),
'public_notes',
serializers.serialize(object.publicNotes,
specifiedType: const FullType(String)),
'client_id',
serializers.serialize(object.clientId,
specifiedType: const FullType(int)),
];
if (object.isChanged != null) {
result
..add('isChanged')
..add(serializers.serialize(object.isChanged,
specifiedType: const FullType(bool)));
}
if (object.createdAt != null) {
result
..add('created_at')
..add(serializers.serialize(object.createdAt,
specifiedType: const FullType(int)));
}
if (object.updatedAt != null) {
result
..add('updated_at')
..add(serializers.serialize(object.updatedAt,
specifiedType: const FullType(int)));
}
if (object.archivedAt != null) {
result
..add('archived_at')
..add(serializers.serialize(object.archivedAt,
specifiedType: const FullType(int)));
}
if (object.isDeleted != null) {
result
..add('is_deleted')
..add(serializers.serialize(object.isDeleted,
specifiedType: const FullType(bool)));
}
if (object.createdUserId != null) {
result
..add('user_id')
..add(serializers.serialize(object.createdUserId,
specifiedType: const FullType(String)));
}
if (object.assignedUserId != null) {
result
..add('assigned_user_id')
..add(serializers.serialize(object.assignedUserId,
specifiedType: const FullType(String)));
}
if (object.subEntityType != null) {
result
..add('entity_type')
..add(serializers.serialize(object.subEntityType,
specifiedType: const FullType(String)));
}
if (object.id != null) {
result
..add('id')
..add(serializers.serialize(object.id,
specifiedType: const FullType(String)));
}
return result;
}
@override
CreditEntity deserialize(Serializers serializers, Iterable<Object> serialized,
{FullType specifiedType = FullType.unspecified}) {
final result = new CreditEntityBuilder();
final iterator = serialized.iterator;
while (iterator.moveNext()) {
final key = iterator.current as String;
iterator.moveNext();
final dynamic value = iterator.current;
switch (key) {
case 'amount':
result.amount = serializers.deserialize(value,
specifiedType: const FullType(double)) as double;
break;
case 'balance':
result.balance = serializers.deserialize(value,
specifiedType: const FullType(double)) as double;
break;
case 'credit_date':
result.creditDate = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
case 'credit_number':
result.creditNumber = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
case 'private_notes':
result.privateNotes = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
case 'public_notes':
result.publicNotes = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
case 'client_id':
result.clientId = serializers.deserialize(value,
specifiedType: const FullType(int)) as int;
break;
case 'isChanged':
result.isChanged = serializers.deserialize(value,
specifiedType: const FullType(bool)) as bool;
break;
case 'created_at':
result.createdAt = serializers.deserialize(value,
specifiedType: const FullType(int)) as int;
break;
case 'updated_at':
result.updatedAt = serializers.deserialize(value,
specifiedType: const FullType(int)) as int;
break;
case 'archived_at':
result.archivedAt = serializers.deserialize(value,
specifiedType: const FullType(int)) as int;
break;
case 'is_deleted':
result.isDeleted = serializers.deserialize(value,
specifiedType: const FullType(bool)) as bool;
break;
case 'user_id':
result.createdUserId = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
case 'assigned_user_id':
result.assignedUserId = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
case 'entity_type':
result.subEntityType = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
case 'id':
result.id = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
specifiedType: const FullType(InvoiceEntity)) as InvoiceEntity);
break;
}
}
@ -273,7 +100,7 @@ class _$CreditEntitySerializer implements StructuredSerializer<CreditEntity> {
class _$CreditListResponse extends CreditListResponse {
@override
final BuiltList<CreditEntity> data;
final BuiltList<InvoiceEntity> data;
factory _$CreditListResponse(
[void Function(CreditListResponseBuilder) updates]) =>
@ -317,10 +144,10 @@ class CreditListResponseBuilder
implements Builder<CreditListResponse, CreditListResponseBuilder> {
_$CreditListResponse _$v;
ListBuilder<CreditEntity> _data;
ListBuilder<CreditEntity> get data =>
_$this._data ??= new ListBuilder<CreditEntity>();
set data(ListBuilder<CreditEntity> data) => _$this._data = data;
ListBuilder<InvoiceEntity> _data;
ListBuilder<InvoiceEntity> get data =>
_$this._data ??= new ListBuilder<InvoiceEntity>();
set data(ListBuilder<InvoiceEntity> data) => _$this._data = data;
CreditListResponseBuilder();
@ -368,7 +195,7 @@ class CreditListResponseBuilder
class _$CreditItemResponse extends CreditItemResponse {
@override
final CreditEntity data;
final InvoiceEntity data;
factory _$CreditItemResponse(
[void Function(CreditItemResponseBuilder) updates]) =>
@ -412,9 +239,9 @@ class CreditItemResponseBuilder
implements Builder<CreditItemResponse, CreditItemResponseBuilder> {
_$CreditItemResponse _$v;
CreditEntityBuilder _data;
CreditEntityBuilder get data => _$this._data ??= new CreditEntityBuilder();
set data(CreditEntityBuilder data) => _$this._data = data;
InvoiceEntityBuilder _data;
InvoiceEntityBuilder get data => _$this._data ??= new InvoiceEntityBuilder();
set data(InvoiceEntityBuilder data) => _$this._data = data;
CreditItemResponseBuilder();
@ -460,308 +287,4 @@ class CreditItemResponseBuilder
}
}
class _$CreditEntity extends CreditEntity {
@override
final double amount;
@override
final double balance;
@override
final String creditDate;
@override
final String creditNumber;
@override
final String privateNotes;
@override
final String publicNotes;
@override
final int clientId;
@override
final bool isChanged;
@override
final int createdAt;
@override
final int updatedAt;
@override
final int archivedAt;
@override
final bool isDeleted;
@override
final String createdUserId;
@override
final String assignedUserId;
@override
final String subEntityType;
@override
final String id;
factory _$CreditEntity([void Function(CreditEntityBuilder) updates]) =>
(new CreditEntityBuilder()..update(updates)).build();
_$CreditEntity._(
{this.amount,
this.balance,
this.creditDate,
this.creditNumber,
this.privateNotes,
this.publicNotes,
this.clientId,
this.isChanged,
this.createdAt,
this.updatedAt,
this.archivedAt,
this.isDeleted,
this.createdUserId,
this.assignedUserId,
this.subEntityType,
this.id})
: super._() {
if (amount == null) {
throw new BuiltValueNullFieldError('CreditEntity', 'amount');
}
if (balance == null) {
throw new BuiltValueNullFieldError('CreditEntity', 'balance');
}
if (creditDate == null) {
throw new BuiltValueNullFieldError('CreditEntity', 'creditDate');
}
if (creditNumber == null) {
throw new BuiltValueNullFieldError('CreditEntity', 'creditNumber');
}
if (privateNotes == null) {
throw new BuiltValueNullFieldError('CreditEntity', 'privateNotes');
}
if (publicNotes == null) {
throw new BuiltValueNullFieldError('CreditEntity', 'publicNotes');
}
if (clientId == null) {
throw new BuiltValueNullFieldError('CreditEntity', 'clientId');
}
}
@override
CreditEntity rebuild(void Function(CreditEntityBuilder) updates) =>
(toBuilder()..update(updates)).build();
@override
CreditEntityBuilder toBuilder() => new CreditEntityBuilder()..replace(this);
@override
bool operator ==(Object other) {
if (identical(other, this)) return true;
return other is CreditEntity &&
amount == other.amount &&
balance == other.balance &&
creditDate == other.creditDate &&
creditNumber == other.creditNumber &&
privateNotes == other.privateNotes &&
publicNotes == other.publicNotes &&
clientId == other.clientId &&
isChanged == other.isChanged &&
createdAt == other.createdAt &&
updatedAt == other.updatedAt &&
archivedAt == other.archivedAt &&
isDeleted == other.isDeleted &&
createdUserId == other.createdUserId &&
assignedUserId == other.assignedUserId &&
subEntityType == other.subEntityType &&
id == other.id;
}
@override
int get hashCode {
return $jf($jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc(
$jc(
0,
amount
.hashCode),
balance
.hashCode),
creditDate
.hashCode),
creditNumber.hashCode),
privateNotes.hashCode),
publicNotes.hashCode),
clientId.hashCode),
isChanged.hashCode),
createdAt.hashCode),
updatedAt.hashCode),
archivedAt.hashCode),
isDeleted.hashCode),
createdUserId.hashCode),
assignedUserId.hashCode),
subEntityType.hashCode),
id.hashCode));
}
@override
String toString() {
return (newBuiltValueToStringHelper('CreditEntity')
..add('amount', amount)
..add('balance', balance)
..add('creditDate', creditDate)
..add('creditNumber', creditNumber)
..add('privateNotes', privateNotes)
..add('publicNotes', publicNotes)
..add('clientId', clientId)
..add('isChanged', isChanged)
..add('createdAt', createdAt)
..add('updatedAt', updatedAt)
..add('archivedAt', archivedAt)
..add('isDeleted', isDeleted)
..add('createdUserId', createdUserId)
..add('assignedUserId', assignedUserId)
..add('subEntityType', subEntityType)
..add('id', id))
.toString();
}
}
class CreditEntityBuilder
implements Builder<CreditEntity, CreditEntityBuilder> {
_$CreditEntity _$v;
double _amount;
double get amount => _$this._amount;
set amount(double amount) => _$this._amount = amount;
double _balance;
double get balance => _$this._balance;
set balance(double balance) => _$this._balance = balance;
String _creditDate;
String get creditDate => _$this._creditDate;
set creditDate(String creditDate) => _$this._creditDate = creditDate;
String _creditNumber;
String get creditNumber => _$this._creditNumber;
set creditNumber(String creditNumber) => _$this._creditNumber = creditNumber;
String _privateNotes;
String get privateNotes => _$this._privateNotes;
set privateNotes(String privateNotes) => _$this._privateNotes = privateNotes;
String _publicNotes;
String get publicNotes => _$this._publicNotes;
set publicNotes(String publicNotes) => _$this._publicNotes = publicNotes;
int _clientId;
int get clientId => _$this._clientId;
set clientId(int clientId) => _$this._clientId = clientId;
bool _isChanged;
bool get isChanged => _$this._isChanged;
set isChanged(bool isChanged) => _$this._isChanged = isChanged;
int _createdAt;
int get createdAt => _$this._createdAt;
set createdAt(int createdAt) => _$this._createdAt = createdAt;
int _updatedAt;
int get updatedAt => _$this._updatedAt;
set updatedAt(int updatedAt) => _$this._updatedAt = updatedAt;
int _archivedAt;
int get archivedAt => _$this._archivedAt;
set archivedAt(int archivedAt) => _$this._archivedAt = archivedAt;
bool _isDeleted;
bool get isDeleted => _$this._isDeleted;
set isDeleted(bool isDeleted) => _$this._isDeleted = isDeleted;
String _createdUserId;
String get createdUserId => _$this._createdUserId;
set createdUserId(String createdUserId) =>
_$this._createdUserId = createdUserId;
String _assignedUserId;
String get assignedUserId => _$this._assignedUserId;
set assignedUserId(String assignedUserId) =>
_$this._assignedUserId = assignedUserId;
String _subEntityType;
String get subEntityType => _$this._subEntityType;
set subEntityType(String subEntityType) =>
_$this._subEntityType = subEntityType;
String _id;
String get id => _$this._id;
set id(String id) => _$this._id = id;
CreditEntityBuilder();
CreditEntityBuilder get _$this {
if (_$v != null) {
_amount = _$v.amount;
_balance = _$v.balance;
_creditDate = _$v.creditDate;
_creditNumber = _$v.creditNumber;
_privateNotes = _$v.privateNotes;
_publicNotes = _$v.publicNotes;
_clientId = _$v.clientId;
_isChanged = _$v.isChanged;
_createdAt = _$v.createdAt;
_updatedAt = _$v.updatedAt;
_archivedAt = _$v.archivedAt;
_isDeleted = _$v.isDeleted;
_createdUserId = _$v.createdUserId;
_assignedUserId = _$v.assignedUserId;
_subEntityType = _$v.subEntityType;
_id = _$v.id;
_$v = null;
}
return this;
}
@override
void replace(CreditEntity other) {
if (other == null) {
throw new ArgumentError.notNull('other');
}
_$v = other as _$CreditEntity;
}
@override
void update(void Function(CreditEntityBuilder) updates) {
if (updates != null) updates(this);
}
@override
_$CreditEntity build() {
final _$result = _$v ??
new _$CreditEntity._(
amount: amount,
balance: balance,
creditDate: creditDate,
creditNumber: creditNumber,
privateNotes: privateNotes,
publicNotes: publicNotes,
clientId: clientId,
isChanged: isChanged,
createdAt: createdAt,
updatedAt: updatedAt,
archivedAt: archivedAt,
isDeleted: isDeleted,
createdUserId: createdUserId,
assignedUserId: assignedUserId,
subEntityType: subEntityType,
id: id);
replace(_$result);
return _$result;
}
}
// ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new

View File

@ -484,7 +484,7 @@ abstract class ActivityEntity
ClientEntity client,
InvoiceEntity invoice,
PaymentEntity payment,
CreditEntity credit,
InvoiceEntity credit,
InvoiceEntity quote,
TaskEntity task,
ExpenseEntity expense,

View File

@ -36,6 +36,9 @@ import 'package:invoiceninja_flutter/redux/payment/payment_state.dart';
import 'package:invoiceninja_flutter/redux/quote/quote_state.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/data/models/credit_model.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_state.dart';
import 'package:invoiceninja_flutter/data/models/user_model.dart';
import 'package:invoiceninja_flutter/redux/user/user_state.dart';
import 'package:invoiceninja_flutter/redux/tax_rate/tax_rate_state.dart';
@ -109,6 +112,8 @@ part 'serializers.g.dart';
TaxRateItemResponse,
TaxRateListResponse,
// STARTER: serializers - do not remove comment
InvoiceEntity,
PaymentableEntity,
UserEntity,
UserListResponse,

View File

@ -29,9 +29,10 @@ Serializers _$serializers = (new Serializers().toBuilder()
..add(CountryEntity.serializer)
..add(CountryItemResponse.serializer)
..add(CountryListResponse.serializer)
..add(CreditEntity.serializer)
..add(CreditItemResponse.serializer)
..add(CreditListResponse.serializer)
..add(CreditState.serializer)
..add(CreditUIState.serializer)
..add(CurrencyEntity.serializer)
..add(CurrencyItemResponse.serializer)
..add(CurrencyListResponse.serializer)
@ -177,9 +178,6 @@ Serializers _$serializers = (new Serializers().toBuilder()
..addBuilderFactory(
const FullType(BuiltList, const [const FullType(CountryEntity)]),
() => new ListBuilder<CountryEntity>())
..addBuilderFactory(
const FullType(BuiltList, const [const FullType(CreditEntity)]),
() => new ListBuilder<CreditEntity>())
..addBuilderFactory(
const FullType(BuiltList, const [const FullType(CurrencyEntity)]),
() => new ListBuilder<CurrencyEntity>())
@ -343,6 +341,7 @@ Serializers _$serializers = (new Serializers().toBuilder()
() => new ListBuilder<InvoiceDesignEntity>())
..addBuilderFactory(
const FullType(BuiltList, const [const FullType(InvoiceEntity)]), () => new ListBuilder<InvoiceEntity>())
..addBuilderFactory(const FullType(BuiltList, const [const FullType(InvoiceEntity)]), () => new ListBuilder<InvoiceEntity>())
..addBuilderFactory(const FullType(BuiltList, const [const FullType(InvoiceItemEntity)]), () => new ListBuilder<InvoiceItemEntity>())
..addBuilderFactory(const FullType(BuiltList, const [const FullType(InvitationEntity)]), () => new ListBuilder<InvitationEntity>())
..addBuilderFactory(const FullType(BuiltList, const [const FullType(LanguageEntity)]), () => new ListBuilder<LanguageEntity>())
@ -395,6 +394,8 @@ Serializers _$serializers = (new Serializers().toBuilder()
..addBuilderFactory(const FullType(BuiltList, const [const FullType(String)]), () => new ListBuilder<String>())
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(InvoiceEntity)]), () => new MapBuilder<String, InvoiceEntity>())
..addBuilderFactory(const FullType(BuiltList, const [const FullType(String)]), () => new ListBuilder<String>())
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(InvoiceEntity)]), () => new MapBuilder<String, InvoiceEntity>())
..addBuilderFactory(const FullType(BuiltList, const [const FullType(String)]), () => new ListBuilder<String>())
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(PaymentEntity)]), () => new MapBuilder<String, PaymentEntity>())
..addBuilderFactory(const FullType(BuiltList, const [const FullType(String)]), () => new ListBuilder<String>())
..addBuilderFactory(const FullType(BuiltMap, const [const FullType(String), const FullType(ProductEntity)]), () => new MapBuilder<String, ProductEntity>())

View File

@ -0,0 +1,84 @@
import 'dart:async';
import 'dart:convert';
import 'dart:core';
import 'package:invoiceninja_flutter/.env.dart';
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/app/app_state.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/data/web_client.dart';
class CreditRepository {
const CreditRepository({
this.webClient = const WebClient(),
});
final WebClient webClient;
Future<InvoiceEntity> loadItem(
Credentials credentials, String entityId) async {
final dynamic response = await webClient.get(
'${credentials.url}/credits/$entityId', credentials.token);
final CreditItemResponse creditResponse =
serializers.deserializeWith(CreditItemResponse.serializer, response);
return creditResponse.data;
}
Future<BuiltList<InvoiceEntity>> loadList(
Credentials credentials, int updatedAt) async {
String url = credentials.url + '/credits?';
if (updatedAt > 0) {
url += '&updated_at=${updatedAt - kUpdatedAtBufferSeconds}';
}
final dynamic response = await webClient.get(url, credentials.token);
final CreditListResponse creditResponse =
serializers.deserializeWith(CreditListResponse.serializer, response);
return creditResponse.data;
}
Future<List<InvoiceEntity>> bulkAction(
Credentials credentials, List<String> ids, EntityAction action) async {
var url = credentials.url + '/credits/bulk?';
if (action != null) {
url += '&action=' + action.toString();
}
final dynamic response = await webClient.post(url, credentials.token,
data: json.encode({'ids': ids}));
final CreditListResponse invoiceResponse =
serializers.deserializeWith(CreditListResponse.serializer, response);
return invoiceResponse.data.toList();
}
Future<InvoiceEntity> saveData(Credentials credentials, InvoiceEntity credit,
[EntityAction action]) async {
final data = serializers.serializeWith(InvoiceEntity.serializer, credit);
dynamic response;
if (credit.isNew) {
response = await webClient.post(
credentials.url + '/credits', credentials.token,
data: json.encode(data));
} else {
var url = credentials.url + '/credits/' + credit.id.toString();
if (action != null) {
url += '?action=' + action.toString();
}
response =
await webClient.put(url, credentials.token, data: json.encode(data));
}
final CreditItemResponse creditResponse =
serializers.deserializeWith(CreditItemResponse.serializer, response);
return creditResponse.data;
}
}

View File

@ -49,6 +49,12 @@ import 'package:sentry/sentry.dart';
import 'package:shared_preferences/shared_preferences.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/ui/credit/credit_screen.dart';
import 'package:invoiceninja_flutter/ui/credit/edit/credit_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/credit/view/credit_view_vm.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_middleware.dart';
import 'package:invoiceninja_flutter/ui/user/user_screen.dart';
import 'package:invoiceninja_flutter/ui/user/edit/user_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/user/view/user_view_vm.dart';
@ -94,6 +100,7 @@ void main({bool isTesting = false}) async {
..addAll(createStoreSettingsMiddleware())
..addAll(createStoreReportsMiddleware())
// STARTER: middleware - do not remove comment
..addAll(createStoreCreditsMiddleware())
..addAll(createStoreUsersMiddleware())
..addAll(createStoreTaxRatesMiddleware())
..addAll(createStoreCompanyGatewaysMiddleware())
@ -344,6 +351,10 @@ class InvoiceNinjaAppState extends State<InvoiceNinjaApp> {
QuoteEditScreen.route: (context) => QuoteEditScreen(),
QuoteEmailScreen.route: (context) => QuoteEmailScreen(),
// STARTER: routes - do not remove comment
CreditScreen.route: (context) => CreditScreen(),
CreditViewScreen.route: (context) => CreditViewScreen(),
CreditEditScreen.route: (context) => CreditEditScreen(),
UserScreen.route: (context) => UserScreenBuilder(),
UserViewScreen.route: (context) => UserViewScreen(),
UserEditScreen.route: (context) => UserEditScreen(),

View File

@ -26,6 +26,7 @@ import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/dialogs.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
class PersistUI {}
@ -230,6 +231,12 @@ void filterEntitiesByType({
));
break;
// STARTER: filter - do not remove comment
case EntityType.credit:
store.dispatch(FilterCreditsByEntity(
entityId: filterEntity.id,
entityType: filterEntity.entityType,
));
break;
}
}
@ -299,6 +306,9 @@ void viewEntitiesByType({
store.dispatch(ViewGroupList(navigator: navigator));
break;
// STARTER: view list - do not remove comment
case EntityType.credit:
store.dispatch(ViewCreditList(navigator: navigator));
break;
}
}
@ -423,6 +433,13 @@ void viewEntityById({
));
break;
// STARTER: view - do not remove comment
case EntityType.credit:
store.dispatch(ViewCredit(
creditId: entityId,
navigator: navigator,
force: force,
));
break;
}
}
@ -537,6 +554,13 @@ void createEntityByType(
));
break;
// STARTER: create type - do not remove comment
case EntityType.credit:
store.dispatch(EditCredit(
navigator: navigator,
force: force,
credit: InvoiceEntity(state: state),
));
break;
}
}
@ -668,6 +692,14 @@ void createEntity({
));
break;
// STARTER: create - do not remove comment
case EntityType.credit:
store.dispatch(EditCredit(
navigator: navigator,
credit: entity,
force: force,
completer: completer,
));
break;
}
}
@ -850,6 +882,18 @@ void editEntityById(
));
break;
// STARTER: edit - do not remove comment
case EntityType.credit:
store.dispatch(EditCredit(
credit: map[entityId],
navigator: navigator,
completer: completer ??
snackBarCompleter<InvoiceEntity>(
context,
entity.isNew
? localization.createdCredit
: localization.updatedCredit),
));
break;
}
}
@ -920,5 +964,8 @@ void handleEntitiesActions(
handleDocumentAction(context, entities, action);
break;
// STARTER: actions - do not remove comment
case EntityType.credit:
handleCreditAction(context, entities, action);
break;
}
}

View File

@ -18,6 +18,7 @@ import 'package:invoiceninja_flutter/redux/auth/auth_reducer.dart';
import 'package:invoiceninja_flutter/redux/company/company_reducer.dart';
import 'package:invoiceninja_flutter/redux/static/static_reducer.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
// We create the State reducer by combining many smaller reducers into one!
AppState appReducer(AppState state, dynamic action) {
@ -84,6 +85,10 @@ final lastErrorReducer = combineReducers<String>([
return '${action.error}';
}),
// STARTER: errors - do not remove comment
TypedReducer<String, LoadCreditsFailure>((state, action) {
return '${action.error}';
}),
TypedReducer<String, RefreshDataFailure>((state, action) {
return '${action.error}';
}),

View File

@ -41,6 +41,8 @@ import 'package:invoiceninja_flutter/ui/group/edit/group_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/product/edit/product_edit_vm.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/credit/credit_state.dart';
import 'package:invoiceninja_flutter/redux/user/user_state.dart';
import 'package:invoiceninja_flutter/redux/tax_rate/tax_rate_state.dart';
import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_state.dart';
@ -160,6 +162,9 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
case EntityType.invoice:
return invoiceState.map;
// STARTER: states switch map - do not remove comment
case EntityType.credit:
return creditState.map;
case EntityType.user:
return userState.map;
case EntityType.taxRate:
@ -217,6 +222,9 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
case EntityType.invoice:
return invoiceState.list;
// STARTER: states switch list - do not remove comment
case EntityType.credit:
return creditState.list;
case EntityType.user:
return userState.list;
case EntityType.taxRate:
@ -253,6 +261,9 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
case EntityType.invoice:
return invoiceUIState;
// STARTER: states switch - do not remove comment
case EntityType.credit:
return creditUIState;
case EntityType.user:
return userUIState;
case EntityType.taxRate:
@ -303,6 +314,10 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
ListUIState get invoiceListState => uiState.invoiceUIState.listUIState;
// STARTER: state getters - do not remove comment
CreditState get creditState => userCompanyState.creditState;
ListUIState get creditListState => uiState.creditUIState.listUIState;
CreditUIState get creditUIState => uiState.creditUIState;
UserState get userState => userCompanyState.userState;
ListUIState get userListState => uiState.userUIState.listUIState;

View File

@ -283,16 +283,6 @@ class FilterClientsByCustom4 implements PersistUI {
void handleClientAction(
BuildContext context, List<BaseEntity> clients, EntityAction action) {
assert(
[
EntityAction.restore,
EntityAction.archive,
EntityAction.delete,
EntityAction.toggleMultiselect
].contains(action) ||
clients.length <= 1,
'Cannot perform this action on more than one client');
if (clients.isEmpty) {
return;
}

View File

@ -18,6 +18,8 @@ import 'package:invoiceninja_flutter/redux/payment/payment_reducer.dart';
import 'package:invoiceninja_flutter/redux/quote/quote_reducer.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/credit/credit_reducer.dart';
import 'package:invoiceninja_flutter/redux/user/user_reducer.dart';
import 'package:invoiceninja_flutter/redux/tax_rate/tax_rate_reducer.dart';
import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_reducer.dart';
@ -38,6 +40,7 @@ UserCompanyState companyReducer(UserCompanyState state, dynamic action) {
..vendorState.replace(vendorsReducer(state.vendorState, action))
..taskState.replace(tasksReducer(state.taskState, action))
// STARTER: reducer - do not remove comment
..creditState.replace(creditsReducer(state.creditState, action))
..userState.replace(usersReducer(state.userState, action))
..taxRateState.replace(taxRatesReducer(state.taxRateState, action))
..companyGatewayState

View File

@ -7,6 +7,8 @@ import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:built_value/built_value.dart';
import 'package:built_value/serializer.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/credit/credit_state.dart';
import 'package:invoiceninja_flutter/redux/user/user_state.dart';
import 'package:invoiceninja_flutter/redux/tax_rate/tax_rate_state.dart';
import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_state.dart';
@ -37,6 +39,8 @@ abstract class UserCompanyState
paymentState: PaymentState(),
quoteState: QuoteState(),
// STARTER: constructor - do not remove comment
creditState: CreditState(),
userState: UserState(),
taxRateState: TaxRateState(),
companyGatewayState: CompanyGatewayState(),
@ -70,6 +74,8 @@ abstract class UserCompanyState
QuoteState get quoteState;
// STARTER: fields - do not remove comment
CreditState get creditState;
UserState get userState;
TaxRateState get taxRateState;

View File

@ -52,6 +52,9 @@ class _$UserCompanyStateSerializer
'quoteState',
serializers.serialize(object.quoteState,
specifiedType: const FullType(QuoteState)),
'creditState',
serializers.serialize(object.creditState,
specifiedType: const FullType(CreditState)),
'userState',
serializers.serialize(object.userState,
specifiedType: const FullType(UserState)),
@ -131,6 +134,10 @@ class _$UserCompanyStateSerializer
result.quoteState.replace(serializers.deserialize(value,
specifiedType: const FullType(QuoteState)) as QuoteState);
break;
case 'creditState':
result.creditState.replace(serializers.deserialize(value,
specifiedType: const FullType(CreditState)) as CreditState);
break;
case 'userState':
result.userState.replace(serializers.deserialize(value,
specifiedType: const FullType(UserState)) as UserState);
@ -314,6 +321,8 @@ class _$UserCompanyState extends UserCompanyState {
@override
final QuoteState quoteState;
@override
final CreditState creditState;
@override
final UserState userState;
@override
final TaxRateState taxRateState;
@ -338,6 +347,7 @@ class _$UserCompanyState extends UserCompanyState {
this.projectState,
this.paymentState,
this.quoteState,
this.creditState,
this.userState,
this.taxRateState,
this.companyGatewayState,
@ -373,6 +383,9 @@ class _$UserCompanyState extends UserCompanyState {
if (quoteState == null) {
throw new BuiltValueNullFieldError('UserCompanyState', 'quoteState');
}
if (creditState == null) {
throw new BuiltValueNullFieldError('UserCompanyState', 'creditState');
}
if (userState == null) {
throw new BuiltValueNullFieldError('UserCompanyState', 'userState');
}
@ -411,6 +424,7 @@ class _$UserCompanyState extends UserCompanyState {
projectState == other.projectState &&
paymentState == other.paymentState &&
quoteState == other.quoteState &&
creditState == other.creditState &&
userState == other.userState &&
taxRateState == other.taxRateState &&
companyGatewayState == other.companyGatewayState &&
@ -434,20 +448,23 @@ class _$UserCompanyState extends UserCompanyState {
$jc(
$jc(
$jc(
0,
userCompany
$jc(
0,
userCompany
.hashCode),
documentState
.hashCode),
documentState
productState
.hashCode),
productState.hashCode),
clientState.hashCode),
invoiceState.hashCode),
expenseState.hashCode),
vendorState.hashCode),
taskState.hashCode),
projectState.hashCode),
paymentState.hashCode),
quoteState.hashCode),
clientState.hashCode),
invoiceState.hashCode),
expenseState.hashCode),
vendorState.hashCode),
taskState.hashCode),
projectState.hashCode),
paymentState.hashCode),
quoteState.hashCode),
creditState.hashCode),
userState.hashCode),
taxRateState.hashCode),
companyGatewayState.hashCode),
@ -468,6 +485,7 @@ class _$UserCompanyState extends UserCompanyState {
..add('projectState', projectState)
..add('paymentState', paymentState)
..add('quoteState', quoteState)
..add('creditState', creditState)
..add('userState', userState)
..add('taxRateState', taxRateState)
..add('companyGatewayState', companyGatewayState)
@ -545,6 +563,12 @@ class UserCompanyStateBuilder
set quoteState(QuoteStateBuilder quoteState) =>
_$this._quoteState = quoteState;
CreditStateBuilder _creditState;
CreditStateBuilder get creditState =>
_$this._creditState ??= new CreditStateBuilder();
set creditState(CreditStateBuilder creditState) =>
_$this._creditState = creditState;
UserStateBuilder _userState;
UserStateBuilder get userState =>
_$this._userState ??= new UserStateBuilder();
@ -583,6 +607,7 @@ class UserCompanyStateBuilder
_projectState = _$v.projectState?.toBuilder();
_paymentState = _$v.paymentState?.toBuilder();
_quoteState = _$v.quoteState?.toBuilder();
_creditState = _$v.creditState?.toBuilder();
_userState = _$v.userState?.toBuilder();
_taxRateState = _$v.taxRateState?.toBuilder();
_companyGatewayState = _$v.companyGatewayState?.toBuilder();
@ -622,6 +647,7 @@ class UserCompanyStateBuilder
projectState: projectState.build(),
paymentState: paymentState.build(),
quoteState: quoteState.build(),
creditState: creditState.build(),
userState: userState.build(),
taxRateState: taxRateState.build(),
companyGatewayState: companyGatewayState.build(),
@ -651,6 +677,8 @@ class UserCompanyStateBuilder
paymentState.build();
_$failedField = 'quoteState';
quoteState.build();
_$failedField = 'creditState';
creditState.build();
_$failedField = 'userState';
userState.build();
_$failedField = 'taxRateState';

View File

@ -0,0 +1,335 @@
import 'dart:async';
import 'package:built_collection/built_collection.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/constants.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
class ViewCreditList extends AbstractNavigatorAction implements PersistUI {
ViewCreditList({
@required NavigatorState navigator,
this.force = false,
}) : super(navigator: navigator);
final bool force;
}
class ViewCredit extends AbstractNavigatorAction
implements PersistUI, PersistPrefs {
ViewCredit({
@required NavigatorState navigator,
@required this.creditId,
this.force = false,
}) : super(navigator: navigator);
final String creditId;
final bool force;
}
class EditCredit extends AbstractNavigatorAction
implements PersistUI, PersistPrefs {
EditCredit(
{@required this.credit,
@required NavigatorState navigator,
this.completer,
this.cancelCompleter,
this.force = false})
: super(navigator: navigator);
final InvoiceEntity credit;
final Completer completer;
final Completer cancelCompleter;
final bool force;
}
class UpdateCredit implements PersistUI {
UpdateCredit(this.credit);
final InvoiceEntity credit;
}
class LoadCredit {
LoadCredit({this.completer, this.creditId});
final Completer completer;
final String creditId;
}
class LoadCreditActivity {
LoadCreditActivity({this.completer, this.creditId});
final Completer completer;
final String creditId;
}
class LoadCredits {
LoadCredits({this.completer, this.force = false});
final Completer completer;
final bool force;
}
class LoadCreditRequest implements StartLoading {}
class LoadCreditFailure implements StopLoading {
LoadCreditFailure(this.error);
final dynamic error;
@override
String toString() {
return 'LoadCreditFailure{error: $error}';
}
}
class LoadCreditSuccess implements StopLoading, PersistData {
LoadCreditSuccess(this.credit);
final InvoiceEntity credit;
@override
String toString() {
return 'LoadCreditSuccess{credit: $credit}';
}
}
class LoadCreditsRequest implements StartLoading {}
class LoadCreditsFailure implements StopLoading {
LoadCreditsFailure(this.error);
final dynamic error;
@override
String toString() {
return 'LoadCreditsFailure{error: $error}';
}
}
class LoadCreditsSuccess implements StopLoading, PersistData {
LoadCreditsSuccess(this.credits);
final BuiltList<InvoiceEntity> credits;
@override
String toString() {
return 'LoadCreditsSuccess{credits: $credits}';
}
}
class SaveCreditRequest implements StartSaving {
SaveCreditRequest({this.completer, this.credit});
final Completer completer;
final InvoiceEntity credit;
}
class SaveCreditSuccess implements StopSaving, PersistData, PersistUI {
SaveCreditSuccess(this.credit);
final InvoiceEntity credit;
}
class AddCreditSuccess implements StopSaving, PersistData, PersistUI {
AddCreditSuccess(this.credit);
final InvoiceEntity credit;
}
class SaveCreditFailure implements StopSaving {
SaveCreditFailure(this.error);
final Object error;
}
class ArchiveCreditsRequest implements StartSaving {
ArchiveCreditsRequest(this.completer, this.creditIds);
final Completer completer;
final List<String> creditIds;
}
class ArchiveCreditsSuccess implements StopSaving, PersistData {
ArchiveCreditsSuccess(this.credits);
final List<InvoiceEntity> credits;
}
class ArchiveCreditsFailure implements StopSaving {
ArchiveCreditsFailure(this.credits);
final List<InvoiceEntity> credits;
}
class DeleteCreditsRequest implements StartSaving {
DeleteCreditsRequest(this.completer, this.creditIds);
final Completer completer;
final List<String> creditIds;
}
class DeleteCreditsSuccess implements StopSaving, PersistData {
DeleteCreditsSuccess(this.credits);
final List<InvoiceEntity> credits;
}
class DeleteCreditsFailure implements StopSaving {
DeleteCreditsFailure(this.credits);
final List<InvoiceEntity> credits;
}
class RestoreCreditsRequest implements StartSaving {
RestoreCreditsRequest(this.completer, this.creditIds);
final Completer completer;
final List<String> creditIds;
}
class RestoreCreditsSuccess implements StopSaving, PersistData {
RestoreCreditsSuccess(this.credits);
final List<InvoiceEntity> credits;
}
class RestoreCreditsFailure implements StopSaving {
RestoreCreditsFailure(this.credits);
final List<InvoiceEntity> credits;
}
class FilterCredits implements PersistUI {
FilterCredits(this.filter);
final String filter;
}
class SortCredits implements PersistUI {
SortCredits(this.field);
final String field;
}
class FilterCreditsByState implements PersistUI {
FilterCreditsByState(this.state);
final EntityState state;
}
class FilterCreditsByCustom1 implements PersistUI {
FilterCreditsByCustom1(this.value);
final String value;
}
class FilterCreditsByCustom2 implements PersistUI {
FilterCreditsByCustom2(this.value);
final String value;
}
class FilterCreditsByCustom3 implements PersistUI {
FilterCreditsByCustom3(this.value);
final String value;
}
class FilterCreditsByCustom4 implements PersistUI {
FilterCreditsByCustom4(this.value);
final String value;
}
class FilterCreditsByEntity implements PersistUI {
FilterCreditsByEntity({this.entityId, this.entityType});
final String entityId;
final EntityType entityType;
}
void handleCreditAction(
BuildContext context, List<BaseEntity> credits, EntityAction action) {
if (credits.isEmpty) {
return;
}
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final CompanyEntity company = state.company;
final localization = AppLocalization.of(context);
final credit = credits.first as InvoiceEntity;
final creditIds = credits.map((credit) => credit.id).toList();
switch (action) {
case EntityAction.edit:
editEntity(context: context, entity: credit);
break;
case EntityAction.restore:
store.dispatch(RestoreCreditsRequest(
snackBarCompleter<Null>(context, localization.restoredCredit),
creditIds));
break;
case EntityAction.archive:
store.dispatch(ArchiveCreditsRequest(
snackBarCompleter<Null>(context, localization.archivedCredit),
creditIds));
break;
case EntityAction.delete:
store.dispatch(DeleteCreditsRequest(
snackBarCompleter<Null>(context, localization.deletedCredit),
creditIds));
break;
case EntityAction.toggleMultiselect:
if (!store.state.creditListState.isInMultiselect()) {
store.dispatch(StartCreditMultiselect(context: context));
}
if (credits.isEmpty) {
break;
}
for (final credit in credits) {
if (!store.state.creditListState.isSelected(credit.id)) {
store.dispatch(
AddToCreditMultiselect(context: context, entity: credit));
} else {
store.dispatch(
RemoveFromCreditMultiselect(context: context, entity: credit));
}
}
break;
}
}
class StartCreditMultiselect {
StartCreditMultiselect({@required this.context});
final BuildContext context;
}
class AddToCreditMultiselect {
AddToCreditMultiselect({@required this.context, @required this.entity});
final BuildContext context;
final BaseEntity entity;
}
class RemoveFromCreditMultiselect {
RemoveFromCreditMultiselect({@required this.context, @required this.entity});
final BuildContext context;
final BaseEntity entity;
}
class ClearCreditMultiselect {
ClearCreditMultiselect({@required this.context});
final BuildContext context;
}

View File

@ -0,0 +1,275 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:redux/redux.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:invoiceninja_flutter/redux/app/app_middleware.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/ui/credit/credit_screen.dart';
import 'package:invoiceninja_flutter/ui/credit/edit/credit_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/credit/view/credit_view_vm.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/data/repositories/credit_repository.dart';
List<Middleware<AppState>> createStoreCreditsMiddleware([
CreditRepository repository = const CreditRepository(),
]) {
final viewCreditList = _viewCreditList();
final viewCredit = _viewCredit();
final editCredit = _editCredit();
final loadCredits = _loadCredits(repository);
final loadCredit = _loadCredit(repository);
final saveCredit = _saveCredit(repository);
final archiveCredit = _archiveCredit(repository);
final deleteCredit = _deleteCredit(repository);
final restoreCredit = _restoreCredit(repository);
return [
TypedMiddleware<AppState, ViewCreditList>(viewCreditList),
TypedMiddleware<AppState, ViewCredit>(viewCredit),
TypedMiddleware<AppState, EditCredit>(editCredit),
TypedMiddleware<AppState, LoadCredits>(loadCredits),
TypedMiddleware<AppState, LoadCredit>(loadCredit),
TypedMiddleware<AppState, SaveCreditRequest>(saveCredit),
TypedMiddleware<AppState, ArchiveCreditsRequest>(archiveCredit),
TypedMiddleware<AppState, DeleteCreditsRequest>(deleteCredit),
TypedMiddleware<AppState, RestoreCreditsRequest>(restoreCredit),
];
}
Middleware<AppState> _editCredit() {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as EditCredit;
if (!action.force &&
hasChanges(store: store, context: action.context, action: action)) {
return;
}
next(action);
store.dispatch(UpdateCurrentRoute(CreditEditScreen.route));
if (isMobile(action.context)) {
action.navigator.pushNamed(CreditEditScreen.route);
}
};
}
Middleware<AppState> _viewCredit() {
return (Store<AppState> store, dynamic dynamicAction,
NextDispatcher next) async {
final action = dynamicAction as ViewCredit;
if (!action.force &&
hasChanges(store: store, context: action.context, action: action)) {
return;
}
next(action);
store.dispatch(UpdateCurrentRoute(CreditViewScreen.route));
if (isMobile(action.context)) {
Navigator.of(action.context).pushNamed(CreditViewScreen.route);
}
};
}
Middleware<AppState> _viewCreditList() {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as ViewCreditList;
if (!action.force &&
hasChanges(store: store, context: action.context, action: action)) {
return;
}
next(action);
if (store.state.creditState.isStale) {
store.dispatch(LoadCredits());
}
store.dispatch(UpdateCurrentRoute(CreditScreen.route));
if (isMobile(action.context)) {
Navigator.of(action.context).pushNamedAndRemoveUntil(
CreditScreen.route, (Route<dynamic> route) => false);
}
};
}
Middleware<AppState> _archiveCredit(CreditRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as ArchiveCreditsRequest;
final prevCredits =
action.creditIds.map((id) => store.state.creditState.map[id]).toList();
repository
.bulkAction(
store.state.credentials, action.creditIds, EntityAction.archive)
.then((List<InvoiceEntity> credits) {
store.dispatch(ArchiveCreditsSuccess(credits));
if (action.completer != null) {
action.completer.complete(null);
}
}).catchError((Object error) {
print(error);
store.dispatch(ArchiveCreditsFailure(prevCredits));
if (action.completer != null) {
action.completer.completeError(error);
}
});
next(action);
};
}
Middleware<AppState> _deleteCredit(CreditRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as DeleteCreditsRequest;
final prevCredits =
action.creditIds.map((id) => store.state.creditState.map[id]).toList();
repository
.bulkAction(
store.state.credentials, action.creditIds, EntityAction.delete)
.then((List<InvoiceEntity> credits) {
store.dispatch(DeleteCreditsSuccess(credits));
if (action.completer != null) {
action.completer.complete(null);
}
}).catchError((Object error) {
print(error);
store.dispatch(DeleteCreditsFailure(prevCredits));
if (action.completer != null) {
action.completer.completeError(error);
}
});
next(action);
};
}
Middleware<AppState> _restoreCredit(CreditRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as RestoreCreditsRequest;
final prevCredits =
action.creditIds.map((id) => store.state.creditState.map[id]).toList();
repository
.bulkAction(
store.state.credentials, action.creditIds, EntityAction.restore)
.then((List<InvoiceEntity> credits) {
store.dispatch(RestoreCreditsSuccess(credits));
if (action.completer != null) {
action.completer.complete(null);
}
}).catchError((Object error) {
print(error);
store.dispatch(RestoreCreditsFailure(prevCredits));
if (action.completer != null) {
action.completer.completeError(error);
}
});
next(action);
};
}
Middleware<AppState> _saveCredit(CreditRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as SaveCreditRequest;
repository
.saveData(store.state.credentials, action.credit)
.then((InvoiceEntity credit) {
if (action.credit.isNew) {
store.dispatch(AddCreditSuccess(credit));
} else {
store.dispatch(SaveCreditSuccess(credit));
}
action.completer.complete(credit);
final creditUIState = store.state.creditUIState;
if (creditUIState.saveCompleter != null) {
creditUIState.saveCompleter.complete(credit);
}
}).catchError((Object error) {
print(error);
store.dispatch(SaveCreditFailure(error));
action.completer.completeError(error);
});
next(action);
};
}
Middleware<AppState> _loadCredit(CreditRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as LoadCredit;
final AppState state = store.state;
if (state.isLoading) {
next(action);
return;
}
store.dispatch(LoadCreditRequest());
repository.loadItem(state.credentials, action.creditId).then((credit) {
store.dispatch(LoadCreditSuccess(credit));
if (action.completer != null) {
action.completer.complete(null);
}
}).catchError((Object error) {
print(error);
store.dispatch(LoadCreditFailure(error));
if (action.completer != null) {
action.completer.completeError(error);
}
});
next(action);
};
}
Middleware<AppState> _loadCredits(CreditRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as LoadCredits;
final AppState state = store.state;
if (!state.creditState.isStale && !action.force) {
next(action);
return;
}
if (state.isLoading) {
next(action);
return;
}
final int updatedAt = (state.creditState.lastUpdated / 1000).round();
store.dispatch(LoadCreditsRequest());
repository.loadList(state.credentials, updatedAt).then((data) {
store.dispatch(LoadCreditsSuccess(data));
if (action.completer != null) {
action.completer.complete(null);
}
/*
if (state.productState.isStale) {
store.dispatch(LoadProducts());
}
*/
}).catchError((Object error) {
print(error);
store.dispatch(LoadCreditsFailure(error));
if (action.completer != null) {
action.completer.completeError(error);
}
});
next(action);
};
}

View File

@ -0,0 +1,288 @@
import 'package:built_collection/built_collection.dart';
import 'package:redux/redux.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.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/credit/credit_actions.dart';
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_state.dart';
import 'package:invoiceninja_flutter/data/models/entities.dart';
EntityUIState creditUIReducer(CreditUIState state, dynamic action) {
return state.rebuild((b) =>
b
..listUIState.replace(creditListReducer(state.listUIState, action))
..editing.replace(editingReducer(state.editing, action))
..selectedId = selectedIdReducer(state.selectedId, action));
}
Reducer<String> selectedIdReducer = combineReducers([
TypedReducer<String, ViewCredit>(
(String selectedId, dynamic action) => action.creditId),
TypedReducer<String, AddCreditSuccess>(
(String selectedId, dynamic action) => action.credit.id),
TypedReducer<String, SelectCompany>((selectedId, action) => ''),
]);
final editingReducer = combineReducers<InvoiceEntity>([
TypedReducer<InvoiceEntity, SaveCreditSuccess>(_updateEditing),
TypedReducer<InvoiceEntity, AddCreditSuccess>(_updateEditing),
TypedReducer<InvoiceEntity, RestoreCreditsSuccess>((credits, action) {
return action.credits[0];
}),
TypedReducer<InvoiceEntity, ArchiveCreditsSuccess>((credits, action) {
return action.credits[0];
}),
TypedReducer<InvoiceEntity, DeleteCreditsSuccess>((credits, action) {
return action.credits[0];
}),
TypedReducer<InvoiceEntity, EditCredit>(_updateEditing),
TypedReducer<InvoiceEntity, UpdateCredit>((credit, action) {
return action.credit.rebuild((b) => b..isChanged = true);
}),
TypedReducer<InvoiceEntity, SelectCompany>(_clearEditing),
TypedReducer<InvoiceEntity, DiscardChanges>(_clearEditing),
]);
InvoiceEntity _clearEditing(InvoiceEntity credit, dynamic action) {
return InvoiceEntity();
}
InvoiceEntity _updateEditing(InvoiceEntity credit, dynamic action) {
return action.credit;
}
final creditListReducer = combineReducers<ListUIState>([
TypedReducer<ListUIState, SortCredits>(_sortCredits),
TypedReducer<ListUIState, FilterCreditsByState>(_filterCreditsByState),
TypedReducer<ListUIState, FilterCredits>(_filterCredits),
TypedReducer<ListUIState, FilterCreditsByCustom1>(_filterCreditsByCustom1),
TypedReducer<ListUIState, FilterCreditsByCustom2>(_filterCreditsByCustom2),
TypedReducer<ListUIState, FilterCreditsByEntity>(_filterCreditsByClient),
TypedReducer<ListUIState, StartCreditMultiselect>(_startListMultiselect),
TypedReducer<ListUIState, AddToCreditMultiselect>(_addToListMultiselect),
TypedReducer<ListUIState, RemoveFromCreditMultiselect>(
_removeFromListMultiselect),
TypedReducer<ListUIState, ClearCreditMultiselect>(_clearListMultiselect),
]);
ListUIState _filterCreditsByClient(ListUIState creditListState,
FilterCreditsByEntity action) {
return creditListState.rebuild((b) =>
b
..filterEntityId = action.entityId
..filterEntityType = action.entityType);
}
ListUIState _filterCreditsByCustom1(ListUIState creditListState,
FilterCreditsByCustom1 action) {
if (creditListState.custom1Filters.contains(action.value)) {
return creditListState
.rebuild((b) => b..custom1Filters.remove(action.value));
} else {
return creditListState.rebuild((b) => b..custom1Filters.add(action.value));
}
}
ListUIState _filterCreditsByCustom2(ListUIState creditListState,
FilterCreditsByCustom2 action) {
if (creditListState.custom2Filters.contains(action.value)) {
return creditListState
.rebuild((b) => b..custom2Filters.remove(action.value));
} else {
return creditListState.rebuild((b) => b..custom2Filters.add(action.value));
}
}
ListUIState _filterCreditsByState(ListUIState creditListState,
FilterCreditsByState action) {
if (creditListState.stateFilters.contains(action.state)) {
return creditListState.rebuild((b) => b..stateFilters.remove(action.state));
} else {
return creditListState.rebuild((b) => b..stateFilters.add(action.state));
}
}
ListUIState _filterCredits(ListUIState creditListState, FilterCredits action) {
return creditListState.rebuild((b) =>
b
..filter = action.filter
..filterClearedAt = action.filter == null
? DateTime
.now()
.millisecondsSinceEpoch
: creditListState.filterClearedAt);
}
ListUIState _sortCredits(ListUIState creditListState, SortCredits action) {
return creditListState.rebuild((b) =>
b
..sortAscending = b.sortField != action.field || !b.sortAscending
..sortField = action.field);
}
ListUIState _startListMultiselect(ListUIState productListState,
StartCreditMultiselect action) {
return productListState.rebuild((b) => b..selectedIds = ListBuilder());
}
ListUIState _addToListMultiselect(ListUIState productListState,
AddToCreditMultiselect action) {
return productListState
.rebuild((b) => b..selectedIds.add(action.entity.id));
}
ListUIState _removeFromListMultiselect(ListUIState productListState,
RemoveFromCreditMultiselect action) {
return productListState
.rebuild((b) => b..selectedIds.remove(action.entity.id));
}
ListUIState _clearListMultiselect(ListUIState productListState,
ClearCreditMultiselect action) {
return productListState.rebuild((b) => b..selectedIds = null);
}
final creditsReducer = combineReducers<CreditState>([
TypedReducer<CreditState, SaveCreditSuccess>(_updateCredit),
TypedReducer<CreditState, AddCreditSuccess>(_addCredit),
TypedReducer<CreditState, LoadCreditsSuccess>(_setLoadedCredits),
TypedReducer<CreditState, LoadCreditSuccess>(_setLoadedCredit),
TypedReducer<CreditState, ArchiveCreditsRequest>(_archiveCreditRequest),
TypedReducer<CreditState, ArchiveCreditsSuccess>(_archiveCreditSuccess),
TypedReducer<CreditState, ArchiveCreditsFailure>(_archiveCreditFailure),
TypedReducer<CreditState, DeleteCreditsRequest>(_deleteCreditRequest),
TypedReducer<CreditState, DeleteCreditsSuccess>(_deleteCreditSuccess),
TypedReducer<CreditState, DeleteCreditsFailure>(_deleteCreditFailure),
TypedReducer<CreditState, RestoreCreditsRequest>(_restoreCreditRequest),
TypedReducer<CreditState, RestoreCreditsSuccess>(_restoreCreditSuccess),
TypedReducer<CreditState, RestoreCreditsFailure>(_restoreCreditFailure),
]);
CreditState _archiveCreditRequest(
CreditState creditState, ArchiveCreditsRequest action) {
final credits = action.creditIds.map((id) => creditState.map[id]).toList();
for (int i = 0; i < credits.length; i++) {
credits[i] = credits[i]
.rebuild((b) => b..archivedAt = DateTime.now().millisecondsSinceEpoch);
}
return creditState.rebuild((b) {
for (final credit in credits) {
b.map[credit.id] = credit;
}
});
}
CreditState _archiveCreditSuccess(
CreditState creditState, ArchiveCreditsSuccess action) {
return creditState.rebuild((b) {
for (final credit in action.credits) {
b.map[credit.id] = credit;
}
});
}
CreditState _archiveCreditFailure(
CreditState creditState, ArchiveCreditsFailure action) {
return creditState.rebuild((b) {
for (final credit in action.credits) {
b.map[credit.id] = credit;
}
});
}
CreditState _deleteCreditRequest(
CreditState creditState, DeleteCreditsRequest action) {
final credits = action.creditIds.map((id) => creditState.map[id]).toList();
for (int i = 0; i < credits.length; i++) {
credits[i] = credits[i].rebuild((b) => b
..archivedAt = DateTime.now().millisecondsSinceEpoch
..isDeleted = true);
}
return creditState.rebuild((b) {
for (final credit in credits) {
b.map[credit.id] = credit;
}
});
}
CreditState _deleteCreditSuccess(
CreditState creditState, DeleteCreditsSuccess action) {
return creditState.rebuild((b) {
for (final credit in action.credits) {
b.map[credit.id] = credit;
}
});
}
CreditState _deleteCreditFailure(
CreditState creditState, DeleteCreditsFailure action) {
return creditState.rebuild((b) {
for (final credit in action.credits) {
b.map[credit.id] = credit;
}
});
}
CreditState _restoreCreditRequest(
CreditState creditState, RestoreCreditsRequest action) {
final credits = action.creditIds.map((id) => creditState.map[id]).toList();
for (int i = 0; i < credits.length; i++) {
credits[i] = credits[i].rebuild((b) => b
..archivedAt = null
..isDeleted = false);
}
return creditState.rebuild((b) {
for (final credit in credits) {
b.map[credit.id] = credit;
}
});
}
CreditState _restoreCreditSuccess(
CreditState creditState, RestoreCreditSuccess action) {
return creditState.rebuild((b) {
for (final credit in action.credits) {
b.map[credit.id] = credit;
}
});
}
CreditState _restoreCreditFailure(
CreditState creditState, RestoreCreditFailure action) {
return creditState.rebuild((b) {
for (final credit in action.credits) {
b.map[credit.id] = credit;
}
});
}
CreditState _addCredit(CreditState creditState, AddCreditSuccess action) {
return creditState.rebuild((b) =>
b
..map[action.credit.id] = action.credit
..list.add(action.credit.id));
}
CreditState _updateCredit(CreditState creditState, SaveCreditSuccess action) {
return creditState.rebuild((b) =>
b
..map[action.credit.id] = action.credit);
}
CreditState _setLoadedCredit(CreditState creditState,
LoadCreditSuccess action) {
return creditState.rebuild((b) =>
b
..map[action.credit.id] = action.credit);
}
CreditState _setLoadedCredits(CreditState creditState,
LoadCreditsSuccess action) =>
creditState.loadCredits(action.credits);

View File

@ -0,0 +1,73 @@
import 'package:invoiceninja_flutter/data/models/credit_model.dart';
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 memoizedDropdownCreditList = memo3(
(BuiltMap<String, InvoiceEntity> creditMap, BuiltList<String> creditList,
String clientId) =>
dropdownCreditsSelector(creditMap, creditList, clientId));
List<String> dropdownCreditsSelector(BuiltMap<String, InvoiceEntity> creditMap,
BuiltList<String> creditList, String clientId) {
final list = creditList.where((creditId) {
final credit = creditMap[creditId];
/*
if (clientId != null && clientId > 0 && credit.clientId != clientId) {
return false;
}
*/
return credit.isActive;
}).toList();
list.sort((creditAId, creditBId) {
final creditA = creditMap[creditAId];
final creditB = creditMap[creditBId];
return creditA.compareTo(creditB, CreditFields.name, true);
});
return list;
}
var memoizedFilteredCreditList = memo3(
(BuiltMap<String, InvoiceEntity> creditMap, BuiltList<String> creditList,
ListUIState creditListState) =>
filteredCreditsSelector(creditMap, creditList, creditListState));
List<String> filteredCreditsSelector(BuiltMap<String, InvoiceEntity> creditMap,
BuiltList<String> creditList, ListUIState creditListState) {
final list = creditList.where((creditId) {
final credit = creditMap[creditId];
if (creditListState.filterEntityId != null &&
credit.entityId != creditListState.filterEntityId) {
return false;
} else {}
if (!credit.matchesStates(creditListState.stateFilters)) {
return false;
}
if (creditListState.custom1Filters.isNotEmpty &&
!creditListState.custom1Filters.contains(credit.customValue1)) {
return false;
}
if (creditListState.custom2Filters.isNotEmpty &&
!creditListState.custom2Filters.contains(credit.customValue2)) {
return false;
}
return credit.matchesFilter(creditListState.filter);
}).toList();
list.sort((creditAId, creditBId) {
final creditA = creditMap[creditAId];
final creditB = creditMap[creditBId];
return creditA.compareTo(
creditB, creditListState.sortField, creditListState.sortAscending);
});
return list;
}
bool hasCreditChanges(
InvoiceEntity credit, BuiltMap<String, InvoiceEntity> creditMap) =>
credit.isNew ? credit.isChanged : credit != creditMap[credit.id];

View File

@ -0,0 +1,75 @@
import 'dart:async';
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/credit_model.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/data/models/models.dart';
part 'credit_state.g.dart';
abstract class CreditState implements Built<CreditState, CreditStateBuilder> {
factory CreditState() {
return _$CreditState._(
lastUpdated: 0,
map: BuiltMap<String, InvoiceEntity>(),
list: BuiltList<String>(),
);
}
CreditState._();
@nullable
int get lastUpdated;
BuiltMap<String, InvoiceEntity> get map;
BuiltList<String> get list;
bool get isStale {
if (!isLoaded) {
return true;
}
return DateTime.now().millisecondsSinceEpoch - lastUpdated >
kMillisecondsToRefreshData;
}
bool get isLoaded => lastUpdated != null && lastUpdated > 0;
CreditState loadCredits(BuiltList<InvoiceEntity> clients) {
final map = Map<String, InvoiceEntity>.fromIterable(
clients,
key: (dynamic item) => item.id,
value: (dynamic item) => item,
);
return rebuild((b) => b
..lastUpdated = DateTime.now().millisecondsSinceEpoch
..map.addAll(map)
..list.replace(map.keys));
}
static Serializer<CreditState> get serializer => _$creditStateSerializer;
}
abstract class CreditUIState extends Object
with EntityUIState
implements Built<CreditUIState, CreditUIStateBuilder> {
factory CreditUIState() {
return _$CreditUIState._(
listUIState: ListUIState(CreditFields.name),
editing: InvoiceEntity(),
selectedId: '',
);
}
CreditUIState._();
@nullable
InvoiceEntity get editing;
@override
bool get isCreatingNew => editing.isNew;
static Serializer<CreditUIState> get serializer => _$creditUIStateSerializer;
}

View File

@ -0,0 +1,406 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'credit_state.dart';
// **************************************************************************
// BuiltValueGenerator
// **************************************************************************
Serializer<CreditState> _$creditStateSerializer = new _$CreditStateSerializer();
Serializer<CreditUIState> _$creditUIStateSerializer =
new _$CreditUIStateSerializer();
class _$CreditStateSerializer implements StructuredSerializer<CreditState> {
@override
final Iterable<Type> types = const [CreditState, _$CreditState];
@override
final String wireName = 'CreditState';
@override
Iterable<Object> serialize(Serializers serializers, CreditState object,
{FullType specifiedType = FullType.unspecified}) {
final result = <Object>[
'map',
serializers.serialize(object.map,
specifiedType: const FullType(BuiltMap,
const [const FullType(String), const FullType(InvoiceEntity)])),
'list',
serializers.serialize(object.list,
specifiedType:
const FullType(BuiltList, const [const FullType(String)])),
];
if (object.lastUpdated != null) {
result
..add('lastUpdated')
..add(serializers.serialize(object.lastUpdated,
specifiedType: const FullType(int)));
}
return result;
}
@override
CreditState deserialize(Serializers serializers, Iterable<Object> serialized,
{FullType specifiedType = FullType.unspecified}) {
final result = new CreditStateBuilder();
final iterator = serialized.iterator;
while (iterator.moveNext()) {
final key = iterator.current as String;
iterator.moveNext();
final dynamic value = iterator.current;
switch (key) {
case 'lastUpdated':
result.lastUpdated = serializers.deserialize(value,
specifiedType: const FullType(int)) as int;
break;
case 'map':
result.map.replace(serializers.deserialize(value,
specifiedType: const FullType(BuiltMap, const [
const FullType(String),
const FullType(InvoiceEntity)
])));
break;
case 'list':
result.list.replace(serializers.deserialize(value,
specifiedType:
const FullType(BuiltList, const [const FullType(String)]))
as BuiltList<Object>);
break;
}
}
return result.build();
}
}
class _$CreditUIStateSerializer implements StructuredSerializer<CreditUIState> {
@override
final Iterable<Type> types = const [CreditUIState, _$CreditUIState];
@override
final String wireName = 'CreditUIState';
@override
Iterable<Object> serialize(Serializers serializers, CreditUIState object,
{FullType specifiedType = FullType.unspecified}) {
final result = <Object>[
'listUIState',
serializers.serialize(object.listUIState,
specifiedType: const FullType(ListUIState)),
];
if (object.editing != null) {
result
..add('editing')
..add(serializers.serialize(object.editing,
specifiedType: const FullType(InvoiceEntity)));
}
if (object.selectedId != null) {
result
..add('selectedId')
..add(serializers.serialize(object.selectedId,
specifiedType: const FullType(String)));
}
return result;
}
@override
CreditUIState deserialize(
Serializers serializers, Iterable<Object> serialized,
{FullType specifiedType = FullType.unspecified}) {
final result = new CreditUIStateBuilder();
final iterator = serialized.iterator;
while (iterator.moveNext()) {
final key = iterator.current as String;
iterator.moveNext();
final dynamic value = iterator.current;
switch (key) {
case 'editing':
result.editing.replace(serializers.deserialize(value,
specifiedType: const FullType(InvoiceEntity)) as InvoiceEntity);
break;
case 'listUIState':
result.listUIState.replace(serializers.deserialize(value,
specifiedType: const FullType(ListUIState)) as ListUIState);
break;
case 'selectedId':
result.selectedId = serializers.deserialize(value,
specifiedType: const FullType(String)) as String;
break;
}
}
return result.build();
}
}
class _$CreditState extends CreditState {
@override
final int lastUpdated;
@override
final BuiltMap<String, InvoiceEntity> map;
@override
final BuiltList<String> list;
factory _$CreditState([void Function(CreditStateBuilder) updates]) =>
(new CreditStateBuilder()..update(updates)).build();
_$CreditState._({this.lastUpdated, this.map, this.list}) : super._() {
if (map == null) {
throw new BuiltValueNullFieldError('CreditState', 'map');
}
if (list == null) {
throw new BuiltValueNullFieldError('CreditState', 'list');
}
}
@override
CreditState rebuild(void Function(CreditStateBuilder) updates) =>
(toBuilder()..update(updates)).build();
@override
CreditStateBuilder toBuilder() => new CreditStateBuilder()..replace(this);
@override
bool operator ==(Object other) {
if (identical(other, this)) return true;
return other is CreditState &&
lastUpdated == other.lastUpdated &&
map == other.map &&
list == other.list;
}
@override
int get hashCode {
return $jf(
$jc($jc($jc(0, lastUpdated.hashCode), map.hashCode), list.hashCode));
}
@override
String toString() {
return (newBuiltValueToStringHelper('CreditState')
..add('lastUpdated', lastUpdated)
..add('map', map)
..add('list', list))
.toString();
}
}
class CreditStateBuilder implements Builder<CreditState, CreditStateBuilder> {
_$CreditState _$v;
int _lastUpdated;
int get lastUpdated => _$this._lastUpdated;
set lastUpdated(int lastUpdated) => _$this._lastUpdated = lastUpdated;
MapBuilder<String, InvoiceEntity> _map;
MapBuilder<String, InvoiceEntity> get map =>
_$this._map ??= new MapBuilder<String, InvoiceEntity>();
set map(MapBuilder<String, InvoiceEntity> map) => _$this._map = map;
ListBuilder<String> _list;
ListBuilder<String> get list => _$this._list ??= new ListBuilder<String>();
set list(ListBuilder<String> list) => _$this._list = list;
CreditStateBuilder();
CreditStateBuilder get _$this {
if (_$v != null) {
_lastUpdated = _$v.lastUpdated;
_map = _$v.map?.toBuilder();
_list = _$v.list?.toBuilder();
_$v = null;
}
return this;
}
@override
void replace(CreditState other) {
if (other == null) {
throw new ArgumentError.notNull('other');
}
_$v = other as _$CreditState;
}
@override
void update(void Function(CreditStateBuilder) updates) {
if (updates != null) updates(this);
}
@override
_$CreditState build() {
_$CreditState _$result;
try {
_$result = _$v ??
new _$CreditState._(
lastUpdated: lastUpdated, map: map.build(), list: list.build());
} catch (_) {
String _$failedField;
try {
_$failedField = 'map';
map.build();
_$failedField = 'list';
list.build();
} catch (e) {
throw new BuiltValueNestedFieldError(
'CreditState', _$failedField, e.toString());
}
rethrow;
}
replace(_$result);
return _$result;
}
}
class _$CreditUIState extends CreditUIState {
@override
final InvoiceEntity editing;
@override
final ListUIState listUIState;
@override
final String selectedId;
@override
final Completer<SelectableEntity> saveCompleter;
@override
final Completer<Null> cancelCompleter;
factory _$CreditUIState([void Function(CreditUIStateBuilder) updates]) =>
(new CreditUIStateBuilder()..update(updates)).build();
_$CreditUIState._(
{this.editing,
this.listUIState,
this.selectedId,
this.saveCompleter,
this.cancelCompleter})
: super._() {
if (listUIState == null) {
throw new BuiltValueNullFieldError('CreditUIState', 'listUIState');
}
}
@override
CreditUIState rebuild(void Function(CreditUIStateBuilder) updates) =>
(toBuilder()..update(updates)).build();
@override
CreditUIStateBuilder toBuilder() => new CreditUIStateBuilder()..replace(this);
@override
bool operator ==(Object other) {
if (identical(other, this)) return true;
return other is CreditUIState &&
editing == other.editing &&
listUIState == other.listUIState &&
selectedId == other.selectedId &&
saveCompleter == other.saveCompleter &&
cancelCompleter == other.cancelCompleter;
}
@override
int get hashCode {
return $jf($jc(
$jc(
$jc($jc($jc(0, editing.hashCode), listUIState.hashCode),
selectedId.hashCode),
saveCompleter.hashCode),
cancelCompleter.hashCode));
}
@override
String toString() {
return (newBuiltValueToStringHelper('CreditUIState')
..add('editing', editing)
..add('listUIState', listUIState)
..add('selectedId', selectedId)
..add('saveCompleter', saveCompleter)
..add('cancelCompleter', cancelCompleter))
.toString();
}
}
class CreditUIStateBuilder
implements Builder<CreditUIState, CreditUIStateBuilder> {
_$CreditUIState _$v;
InvoiceEntityBuilder _editing;
InvoiceEntityBuilder get editing =>
_$this._editing ??= new InvoiceEntityBuilder();
set editing(InvoiceEntityBuilder editing) => _$this._editing = editing;
ListUIStateBuilder _listUIState;
ListUIStateBuilder get listUIState =>
_$this._listUIState ??= new ListUIStateBuilder();
set listUIState(ListUIStateBuilder listUIState) =>
_$this._listUIState = listUIState;
String _selectedId;
String get selectedId => _$this._selectedId;
set selectedId(String selectedId) => _$this._selectedId = selectedId;
Completer<SelectableEntity> _saveCompleter;
Completer<SelectableEntity> get saveCompleter => _$this._saveCompleter;
set saveCompleter(Completer<SelectableEntity> saveCompleter) =>
_$this._saveCompleter = saveCompleter;
Completer<Null> _cancelCompleter;
Completer<Null> get cancelCompleter => _$this._cancelCompleter;
set cancelCompleter(Completer<Null> cancelCompleter) =>
_$this._cancelCompleter = cancelCompleter;
CreditUIStateBuilder();
CreditUIStateBuilder get _$this {
if (_$v != null) {
_editing = _$v.editing?.toBuilder();
_listUIState = _$v.listUIState?.toBuilder();
_selectedId = _$v.selectedId;
_saveCompleter = _$v.saveCompleter;
_cancelCompleter = _$v.cancelCompleter;
_$v = null;
}
return this;
}
@override
void replace(CreditUIState other) {
if (other == null) {
throw new ArgumentError.notNull('other');
}
_$v = other as _$CreditUIState;
}
@override
void update(void Function(CreditUIStateBuilder) updates) {
if (updates != null) updates(this);
}
@override
_$CreditUIState build() {
_$CreditUIState _$result;
try {
_$result = _$v ??
new _$CreditUIState._(
editing: _editing?.build(),
listUIState: listUIState.build(),
selectedId: selectedId,
saveCompleter: saveCompleter,
cancelCompleter: cancelCompleter);
} catch (_) {
String _$failedField;
try {
_$failedField = 'editing';
_editing?.build();
_$failedField = 'listUIState';
listUIState.build();
} catch (e) {
throw new BuiltValueNestedFieldError(
'CreditUIState', _$failedField, e.toString());
}
rethrow;
}
replace(_$result);
return _$result;
}
}
// ignore_for_file: always_put_control_body_on_new_line,always_specify_types,annotate_overrides,avoid_annotating_with_dynamic,avoid_as,avoid_catches_without_on_clauses,avoid_returning_this,lines_longer_than_80_chars,omit_local_variable_types,prefer_expression_function_bodies,sort_constructors_first,test_types_in_equals,unnecessary_const,unnecessary_new

View File

@ -23,6 +23,8 @@ import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/redux/user/user_actions.dart';
import 'package:invoiceninja_flutter/redux/vendor/vendor_actions.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
import 'package:redux/redux.dart';
PrefState prefReducer(
@ -395,6 +397,12 @@ Reducer<BuiltList<HistoryRecord>> historyReducer = combineReducers([
_addToHistory(historyList,
HistoryRecord(id: action.group.id, entityType: EntityType.group))),
// STARTER: history - do not remove comment
TypedReducer<BuiltList<HistoryRecord>, ViewCredit>((historyList, action) =>
_addToHistory(historyList,
HistoryRecord(id: action.creditId, entityType: EntityType.credit))),
TypedReducer<BuiltList<HistoryRecord>, EditCredit>((historyList, action) =>
_addToHistory(historyList,
HistoryRecord(id: action.credit.id, entityType: EntityType.credit))),
]);
BuiltList<HistoryRecord> _addToHistory(

View File

@ -22,6 +22,8 @@ import 'package:invoiceninja_flutter/redux/quote/quote_reducer.dart';
import 'package:invoiceninja_flutter/redux/task/task_reducer.dart';
import 'package:invoiceninja_flutter/redux/vendor/vendor_reducer.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/credit/credit_reducer.dart';
import 'package:invoiceninja_flutter/redux/user/user_reducer.dart';
import 'package:invoiceninja_flutter/redux/tax_rate/tax_rate_reducer.dart';
import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_reducer.dart';
@ -47,6 +49,7 @@ UIState uiReducer(UIState state, dynamic action) {
.replace(dashboardUIReducer(state.dashboardUIState, action))
..reportsUIState.replace(reportsUIReducer(state.reportsUIState, action))
// STARTER: reducer - do not remove comment
..creditUIState.replace(creditUIReducer(state.creditUIState, action))
..userUIState.replace(userUIReducer(state.userUIState, action))
..taxRateUIState.replace(taxRateUIReducer(state.taxRateUIState, action))
..companyGatewayUIState

View File

@ -16,6 +16,8 @@ import 'package:invoiceninja_flutter/redux/task/task_state.dart';
import 'package:invoiceninja_flutter/redux/vendor/vendor_state.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/credit/credit_state.dart';
import 'package:invoiceninja_flutter/redux/user/user_state.dart';
import 'package:invoiceninja_flutter/redux/tax_rate/tax_rate_state.dart';
import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_state.dart';
@ -35,6 +37,8 @@ abstract class UIState implements Built<UIState, UIStateBuilder> {
clientUIState: ClientUIState(),
invoiceUIState: InvoiceUIState(),
// STARTER: constructor - do not remove comment
creditUIState: CreditUIState(),
userUIState: UserUIState(),
taxRateUIState: TaxRateUIState(),
companyGatewayUIState: CompanyGatewayUIState(),
@ -73,6 +77,8 @@ abstract class UIState implements Built<UIState, UIStateBuilder> {
InvoiceUIState get invoiceUIState;
// STARTER: properties - do not remove comment
CreditUIState get creditUIState;
UserUIState get userUIState;
TaxRateUIState get taxRateUIState;

View File

@ -42,6 +42,9 @@ class _$UIStateSerializer implements StructuredSerializer<UIState> {
'invoiceUIState',
serializers.serialize(object.invoiceUIState,
specifiedType: const FullType(InvoiceUIState)),
'creditUIState',
serializers.serialize(object.creditUIState,
specifiedType: const FullType(CreditUIState)),
'userUIState',
serializers.serialize(object.userUIState,
specifiedType: const FullType(UserUIState)),
@ -139,6 +142,10 @@ class _$UIStateSerializer implements StructuredSerializer<UIState> {
result.invoiceUIState.replace(serializers.deserialize(value,
specifiedType: const FullType(InvoiceUIState)) as InvoiceUIState);
break;
case 'creditUIState':
result.creditUIState.replace(serializers.deserialize(value,
specifiedType: const FullType(CreditUIState)) as CreditUIState);
break;
case 'userUIState':
result.userUIState.replace(serializers.deserialize(value,
specifiedType: const FullType(UserUIState)) as UserUIState);
@ -221,6 +228,8 @@ class _$UIState extends UIState {
@override
final InvoiceUIState invoiceUIState;
@override
final CreditUIState creditUIState;
@override
final UserUIState userUIState;
@override
final TaxRateUIState taxRateUIState;
@ -260,6 +269,7 @@ class _$UIState extends UIState {
this.productUIState,
this.clientUIState,
this.invoiceUIState,
this.creditUIState,
this.userUIState,
this.taxRateUIState,
this.companyGatewayUIState,
@ -298,6 +308,9 @@ class _$UIState extends UIState {
if (invoiceUIState == null) {
throw new BuiltValueNullFieldError('UIState', 'invoiceUIState');
}
if (creditUIState == null) {
throw new BuiltValueNullFieldError('UIState', 'creditUIState');
}
if (userUIState == null) {
throw new BuiltValueNullFieldError('UIState', 'userUIState');
}
@ -359,6 +372,7 @@ class _$UIState extends UIState {
productUIState == other.productUIState &&
clientUIState == other.clientUIState &&
invoiceUIState == other.invoiceUIState &&
creditUIState == other.creditUIState &&
userUIState == other.userUIState &&
taxRateUIState == other.taxRateUIState &&
companyGatewayUIState == other.companyGatewayUIState &&
@ -394,13 +408,13 @@ class _$UIState extends UIState {
$jc(
$jc(
$jc(
$jc($jc($jc($jc(0, selectedCompanyIndex.hashCode), currentRoute.hashCode), previousRoute.hashCode),
filter.hashCode),
filterClearedAt.hashCode),
dashboardUIState.hashCode),
productUIState.hashCode),
clientUIState.hashCode),
invoiceUIState.hashCode),
$jc($jc($jc($jc($jc(0, selectedCompanyIndex.hashCode), currentRoute.hashCode), previousRoute.hashCode), filter.hashCode),
filterClearedAt.hashCode),
dashboardUIState.hashCode),
productUIState.hashCode),
clientUIState.hashCode),
invoiceUIState.hashCode),
creditUIState.hashCode),
userUIState.hashCode),
taxRateUIState.hashCode),
companyGatewayUIState.hashCode),
@ -428,6 +442,7 @@ class _$UIState extends UIState {
..add('productUIState', productUIState)
..add('clientUIState', clientUIState)
..add('invoiceUIState', invoiceUIState)
..add('creditUIState', creditUIState)
..add('userUIState', userUIState)
..add('taxRateUIState', taxRateUIState)
..add('companyGatewayUIState', companyGatewayUIState)
@ -495,6 +510,12 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
set invoiceUIState(InvoiceUIStateBuilder invoiceUIState) =>
_$this._invoiceUIState = invoiceUIState;
CreditUIStateBuilder _creditUIState;
CreditUIStateBuilder get creditUIState =>
_$this._creditUIState ??= new CreditUIStateBuilder();
set creditUIState(CreditUIStateBuilder creditUIState) =>
_$this._creditUIState = creditUIState;
UserUIStateBuilder _userUIState;
UserUIStateBuilder get userUIState =>
_$this._userUIState ??= new UserUIStateBuilder();
@ -587,6 +608,7 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
_productUIState = _$v.productUIState?.toBuilder();
_clientUIState = _$v.clientUIState?.toBuilder();
_invoiceUIState = _$v.invoiceUIState?.toBuilder();
_creditUIState = _$v.creditUIState?.toBuilder();
_userUIState = _$v.userUIState?.toBuilder();
_taxRateUIState = _$v.taxRateUIState?.toBuilder();
_companyGatewayUIState = _$v.companyGatewayUIState?.toBuilder();
@ -633,6 +655,7 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
productUIState: productUIState.build(),
clientUIState: clientUIState.build(),
invoiceUIState: invoiceUIState.build(),
creditUIState: creditUIState.build(),
userUIState: userUIState.build(),
taxRateUIState: taxRateUIState.build(),
companyGatewayUIState: companyGatewayUIState.build(),
@ -657,6 +680,8 @@ class UIStateBuilder implements Builder<UIState, UIStateBuilder> {
clientUIState.build();
_$failedField = 'invoiceUIState';
invoiceUIState.build();
_$failedField = 'creditUIState';
creditUIState.build();
_$failedField = 'userUIState';
userUIState.build();
_$failedField = 'taxRateUIState';

View File

@ -29,6 +29,7 @@ import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:url_launcher/url_launcher.dart';
// STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
class MenuDrawer extends StatelessWidget {
const MenuDrawer({
@ -253,6 +254,19 @@ class MenuDrawer extends StatelessWidget {
title: localization.expenses,
),
// STARTER: menu - do not remove comment
DrawerTile(
company: company,
entityType: EntityType.credit,
icon: getEntityIcon(EntityType.credit),
title: localization.credits,
onTap: () => store.dispatch(ViewcreditList(context)),
onCreateTap: () {
navigator.pop();
store.dispatch(EditCredit(
credit: InvoiceEntity(), context: context));
},
),
DrawerTile(
company: company,
icon: getEntityIcon(EntityType.reports),

View File

@ -0,0 +1,134 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart';
import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart';
import 'package:invoiceninja_flutter/ui/app/help_text.dart';
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
import 'package:invoiceninja_flutter/ui/credit/credit_list_item.dart';
import 'package:invoiceninja_flutter/ui/credit/credit_list_vm.dart';
import 'package:invoiceninja_flutter/ui/app/presenters/entity_presenter.dart';
import 'package:invoiceninja_flutter/ui/app/tables/entity_datatable.dart';
import 'package:invoiceninja_flutter/utils/icons.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
class CreditList extends StatefulWidget {
const CreditList({
Key key,
@required this.viewModel,
}) : super(key: key);
final CreditListVM viewModel;
@override
_CreditListState createState() => _CreditListState();
}
class _CreditListState extends State<CreditList> {
EntityDataTableSource dataTableSource;
@override
void initState() {
super.initState();
final viewModel = widget.viewModel;
dataTableSource = EntityDataTableSource(
context: context,
entityType: EntityType.credit,
editingId: viewModel.state.creditUIState.editing.id,
tableColumns: viewModel.tableColumns,
entityList: viewModel.creditList,
entityMap: viewModel.creditMap,
entityPresenter: CreditPresenter(),
onTap: (BaseEntity credit) => viewModel.onCreditTap(context, credit));
}
@override
void didUpdateWidget(CreditList oldWidget) {
super.didUpdateWidget(oldWidget);
final viewModel = widget.viewModel;
dataTableSource.editingId = viewModel.state.creditUIState.editing.id;
dataTableSource.entityList = viewModel.creditList;
dataTableSource.entityMap = viewModel.creditMap;
// ignore: invalid_use_of_visible_for_testing_member, invalid_use_of_protected_member
dataTableSource.notifyListeners();
}
@override
Widget build(BuildContext context) {
/*
final localization = AppLocalization.of(context);
final listState = viewModel.listState;
final filteredClientId = listState.filterEntityId;
final filteredClient =
filteredClientId != null ? viewModel.clientMap[filteredClientId] : null;
*/
final store = StoreProvider.of<AppState>(context);
final listUIState = store.state.uiState.creditUIState.listUIState;
final isInMultiselect = listUIState.isInMultiselect();
final creditList = viewModel.creditList;
if (isNotMobile(context) &&
creditList.isNotEmpty &&
!state.uiState.isEditing &&
!creditList.contains(state.creditUIState.selectedId)) {
viewEntityById(
context: context,
entityType: EntityType.credit,
entityId: creditList.first);
}
return Column(
children: <Widget>[
Expanded(
child: !viewModel.isLoaded
? LoadingIndicator()
: RefreshIndicator(
onRefresh: () => viewModel.onRefreshed(context),
child: viewModel.creditList.isEmpty
? HelpText(AppLocalization.of(context).noRecordsFound)
: ListView.separated(
shrinkWrap: true,
separatorBuilder: (context, index) => ListDivider(),
itemCount: viewModel.creditList.length,
itemBuilder: (BuildContext context, index) {
final creditId = viewModel.creditList[index];
final credit = viewModel.creditMap[creditId];
final userCompany = viewModel.userCompany;
void showDialog() => showEntityActionsDialog(
userCompany: userCompany,
entity: credit,
context: context,
onEntityAction: viewModel.onEntityAction);
return CreditListItem(
user: viewModel.userCompany.user,
filter: viewModel.filter,
credit: credit,
onTap: () =>
viewModel.onCreditTap(context, credit),
onEntityAction: (EntityAction action) {
if (action == EntityAction.more) {
showDialog();
} else {
viewModel.onEntityAction(
context, credit, action);
}
},
onLongPress: () => showDialog(),
);
},
),
),
),
],
);
}
}

View File

@ -0,0 +1,104 @@
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/data/models/credit_model.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/ui/app/entity_state_label.dart';
import 'package:invoiceninja_flutter/utils/formatting.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/app/dismissible_entity.dart';
class CreditListItem extends StatelessWidget {
const CreditListItem({
@required this.user,
@required this.onEntityAction,
@required this.onTap,
@required this.onLongPress,
@required this.credit,
@required this.filter,
this.onCheckboxChanged,
this.isChecked = false,
});
final UserEntity user;
final Function(EntityAction) onEntityAction;
final GestureTapCallback onTap;
final GestureTapCallback onLongPress;
final InvoiceEntity credit;
final String filter;
final Function(bool) onCheckboxChanged;
final bool isChecked;
static final creditItemKey = (int id) => Key('__credit_item_${id}__');
@override
Widget build(BuildContext context) {
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final uiState = state.uiState;
final creditUIState = uiState.creditUIState;
final listUIState = creditUIState.listUIState;
final isInMultiselect = listUIState.isInMultiselect();
final showCheckbox = onCheckboxChanged != null || isInMultiselect;
final filterMatch = filter != null && filter.isNotEmpty
? credit.matchesFilterValue(filter)
: null;
final subtitle = filterMatch;
return DismissibleEntity(
userCompany: state.userCompany,
entity: credit,
isSelected: credit.id ==
(uiState.isEditing
? creditUIState.editing.id
: creditUIState.selectedId),
onEntityAction: onEntityAction,
child: ListTile(
onTap: isInMultiselect
? () => onEntityAction(EntityAction.toggleMultiselect)
: onTap,
onLongPress: onLongPress,
leading: showCheckbox
? IgnorePointer(
ignoring: listUIState.isInMultiselect(),
child: Checkbox(
value: isChecked,
materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
onChanged: (value) => onCheckboxChanged(value),
activeColor: Theme.of(context).accentColor,
),
)
: null,
title: Container(
width: MediaQuery.of(context).size.width,
child: Row(
children: <Widget>[
Expanded(
child: Text(
credit.name,
style: Theme.of(context).textTheme.headline6,
),
),
Text(formatNumber(credit.listDisplayAmount, context),
style: Theme.of(context).textTheme.headline6),
],
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
subtitle != null && subtitle.isNotEmpty
? Text(
subtitle,
maxLines: 3,
overflow: TextOverflow.ellipsis,
)
: Container(),
EntityStateLabel(credit),
],
),
),
);
}
}

View File

@ -0,0 +1,107 @@
import 'dart:async';
import 'package:invoiceninja_flutter/data/models/credit_model.dart';
import 'package:redux/redux.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:built_collection/built_collection.dart';
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_selectors.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/credit/credit_list.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
class CreditListBuilder extends StatelessWidget {
const CreditListBuilder({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, CreditListVM>(
converter: CreditListVM.fromStore,
builder: (context, viewModel) {
return CreditList(
viewModel: viewModel,
);
},
);
}
}
class CreditListVM {
CreditListVM({
@required this.userCompany,
@required this.creditList,
@required this.creditMap,
@required this.filter,
@required this.isLoading,
@required this.isLoaded,
@required this.onCreditTap,
@required this.listState,
@required this.onRefreshed,
@required this.onEntityAction,
@required this.tableColumns,
@required this.onClearEntityFilterPressed,
@required this.onViewEntityFilterPressed,
});
static CreditListVM fromStore(Store<AppState> store) {
Future<Null> _handleRefresh(BuildContext context) {
if (store.state.isLoading) {
return Future<Null>(null);
}
final completer = snackBarCompleter<Null>(
context, AppLocalization.of(context).refreshComplete);
store.dispatch(LoadCredits(completer: completer, force: true));
return completer.future;
}
final state = store.state;
return CreditListVM(
userCompany: state.userCompany,
listState: state.creditListState,
creditList: memoizedFilteredCreditList(
state.creditState.map, state.creditState.list, state.creditListState),
creditMap: state.creditState.map,
isLoading: state.isLoading,
isLoaded: state.creditState.isLoaded,
filter: state.creditUIState.listUIState.filter,
onClearEntityFilterPressed: () => store.dispatch(FilterCreditsByEntity()),
onViewEntityFilterPressed: (BuildContext context) => viewEntityById(
context: context,
entityId: state.creditListState.filterEntityId,
entityType: state.creditListState.filterEntityType),
onCreditTap: (context, credit) {
if (store.state.creditListState.isInMultiselect()) {
handleCreditAction(context, [credit], EntityAction.toggleMultiselect);
} else {
viewEntity(context: context, entity: credit);
}
},
onEntityAction: (BuildContext context, List<BaseEntity> credits,
EntityAction action) =>
handleCreditAction(context, credits, action),
onRefreshed: (context) => _handleRefresh(context),
);
}
final UserCompanyEntity userCompany;
final List<String> creditList;
final BuiltMap<String, InvoiceEntity> creditMap;
final ListUIState listState;
final String filter;
final bool isLoading;
final bool isLoaded;
final Function(BuildContext, InvoiceEntity) onCreditTap;
final Function(BuildContext) onRefreshed;
final Function(BuildContext, List<BaseEntity>, EntityAction) onEntityAction;
final Function onClearEntityFilterPressed;
final Function(BuildContext) onViewEntityFilterPressed;
final List<String> tableColumns;
}

View File

@ -0,0 +1,132 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/ui/app/app_scaffold.dart';
import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart';
import 'package:invoiceninja_flutter/data/models/credit_model.dart';
import 'package:invoiceninja_flutter/ui/app/list_filter.dart';
import 'package:invoiceninja_flutter/ui/app/list_filter_button.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/credit/credit_list_vm.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
import 'package:invoiceninja_flutter/ui/app/app_drawer_vm.dart';
import 'package:invoiceninja_flutter/ui/app/app_bottom_bar.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
class CreditScreen extends StatelessWidget {
const CreditScreen({
Key key,
@required this.viewModel,
}) : super(key: key);
static const String route = '/credit';
final CreditScreenVM viewModel;
@override
Widget build(BuildContext context) {
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final company = state.selectedCompany;
final localization = AppLocalization.of(context);
final listUIState = state.uiState.creditUIState.listUIState;
final isInMultiselect = listUIState.isInMultiselect();
return AppScaffold(
isChecked: isInMultiselect &&
listUIState.selectedIds.length == viewModel.creditList.length,
showCheckbox: isInMultiselect,
onCheckboxChanged: (value) {
final credits = viewModel.creditList
.map<InvoiceEntity>((creditId) => viewModel.creditMap[creditId])
.where((credit) => value != listUIState.isSelected(credit))
.toList();
viewModel.onEntityAction(
context, credits, EntityAction.toggleMultiselect);
},
appBarTitle: ListFilter(
title: localization.credits
key: ValueKey(state.creditListState.filterClearedAt),
entityType: EntityType.credit,
onFilterChanged: (value) {
store.dispatch(FilterCredits(value));
},
),
appBarActions: [
if (!viewModel.isInMultiselect)
ListFilterButton(
entityType: EntityType.credit,
onFilterPressed: (String value) {
store.dispatch(FilterCredits(value));
},
),
if (viewModel.isInMultiselect)
SaveCancelButtons(
saveLabel: localization.done,
onSavePressed: listUIState.selectedIds.isEmpty
? null
: (context) async {
final credits = listUIState.selectedIds
.map<InvoiceEntity>(
(creditId) => viewModel.creditMap[creditId])
.toList();
await showEntityActionsDialog(
entities: credits, context: context, multiselect: true,
completer: Completer<Null>()
..future.then((_) =>
store.dispatch(ClearCreditMultiselect())),
);
},
onCancelPressed: (context) =>
store.dispatch(ClearCreditMultiselect()),
),
],
body: CreditListBuilder(),
bottomNavigationBar: AppBottomBar(
entityType: EntityType.credit,
onSelectedSortField: (value) => store.dispatch(SortCredits(value)),
customValues1: company.getCustomFieldValues(CustomFieldType.credit1,
excludeBlank: true),
customValues2: company.getCustomFieldValues(CustomFieldType.credit2,
excludeBlank: true),
onSelectedCustom1: (value) =>
store.dispatch(FilterCreditsByCustom1(value)),
onSelectedCustom2: (value) =>
store.dispatch(FilterCreditsByCustom2(value)),
sortFields: [
CreditFields.updatedAt,
],
onSelectedState: (EntityState state, value) {
store.dispatch(FilterCreditsByState(state));
},
onCheckboxPressed: () {
if (store.state.creditListState.isInMultiselect()) {
store.dispatch(ClearCreditMultiselect());
} else {
store.dispatch(StartCreditMultiselect());
}
},
),
floatingActionButton: user.canCreate(EntityType.credit)
? FloatingActionButton(
heroTag: 'credit_fab',
backgroundColor: Theme.of(context).primaryColorDark,
onPressed: () {
store.dispatch(
EditCredit(credit: InvoiceEntity(), context: context));
},
child: Icon(
Icons.add,
color: Colors.white,
),
tooltip: localization.newCredit,
)
: null,
);
}
}

View File

@ -0,0 +1,105 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/ui/settings/edit_scaffold.dart';
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
import 'package:invoiceninja_flutter/ui/credit/edit/credit_edit_vm.dart';
import 'package:invoiceninja_flutter/ui/app/buttons/action_icon_button.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
class CreditEdit extends StatefulWidget {
const CreditEdit({
Key key,
@required this.viewModel,
}) : super(key: key);
final CreditEditVM viewModel;
@override
_CreditEditState createState() => _CreditEditState();
}
class _CreditEditState extends State<CreditEdit> {
static final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: '_creditEdit');
final _debouncer = Debouncer();
// STARTER: controllers - do not remove comment
List<TextEditingController> _controllers = [];
@override
void didChangeDependencies() {
_controllers = [
// STARTER: array - do not remove comment
];
_controllers.forEach((controller) => controller.removeListener(_onChanged));
final credit = widget.viewModel.credit;
// STARTER: read value - do not remove comment
_controllers.forEach((controller) => controller.addListener(_onChanged));
super.didChangeDependencies();
}
@override
void dispose() {
_controllers.forEach((controller) {
controller.removeListener(_onChanged);
controller.dispose();
});
super.dispose();
}
void _onChanged() {
_debouncer.run(() {
final credit = widget.viewModel.credit.rebuild((b) => b
// STARTER: set value - do not remove comment
);
if (credit != widget.viewModel.credit) {
widget.viewModel.onChanged(credit);
}
});
}
@override
Widget build(BuildContext context) {
final viewModel = widget.viewModel;
final localization = AppLocalization.of(context);
final credit = viewModel.credit;
return EditScaffold(
onCancelPressed: (context) => viewModel.onCancelPressed(context),
onSavePressed: (context) {
final bool isValid = _formKey.currentState.validate();
setState(() {
_autoValidate = !isValid;
});
if (!isValid) {
return;
}
viewModel.onSavePressed(context);
},
body: Form(
key: _formKey,
child: Builder(builder: (BuildContext context) {
return ListView(
children: <Widget>[
FormCard(
children: <Widget>[
// STARTER: widgets - do not remove comment
],
),
],
);
})),
);
}
}

View File

@ -0,0 +1,105 @@
import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/ui/credit/credit_screen.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:redux/redux.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart';
import 'package:invoiceninja_flutter/ui/credit/view/credit_view_vm.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
import 'package:invoiceninja_flutter/data/models/credit_model.dart';
import 'package:invoiceninja_flutter/ui/credit/edit/credit_edit.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
class CreditEditScreen extends StatelessWidget {
const CreditEditScreen({Key key}) : super(key: key);
static const String route = '/credit/edit';
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, CreditEditVM>(
converter: (Store<AppState> store) {
return CreditEditVM.fromStore(store);
},
builder: (context, viewModel) {
return CreditEdit(
viewModel: viewModel,
key: ValueKey(viewModel.credit.id),
);
},
);
}
}
class CreditEditVM {
CreditEditVM({
@required this.state,
@required this.credit,
@required this.company,
@required this.onChanged,
@required this.isSaving,
@required this.origCredit,
@required this.onSavePressed,
@required this.onCancelPressed,
@required this.isLoading,
});
factory CreditEditVM.fromStore(Store<AppState> store) {
final state = store.state;
final credit = state.creditUIState.editing;
return CreditEditVM(
state: state,
isLoading: state.isLoading,
isSaving: state.isSaving,
origCredit: state.creditState.map[credit.id],
credit: credit,
company: state.selectedCompany,
onChanged: (InvoiceEntity credit) {
store.dispatch(UpdateCredit(credit));
},
onCancelPressed: (BuildContext context) {
store.dispatch(
EditCredit(credit: InvoiceEntity(), context: context, force: true));
store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute));
},
onSavePressed: (BuildContext context) {
final Completer<InvoiceEntity> completer = new Completer<InvoiceEntity>();
store.dispatch(SaveCreditRequest(completer: completer, credit: credit));
return completer.future.then((savedCredit) {
if (isMobile(context)) {
store.dispatch(UpdateCurrentRoute(CreditViewScreen.route));
if (credit.isNew) {
Navigator.of(context)
.pushReplacementNamed(CreditViewScreen.route);
} else {
Navigator.of(context).pop(savedCredit);
}
} else {
store.dispatch(ViewCredit(
context: context, creditId: savedCredit.id, force: true));
}
}).catchError((Object error) {
showDialog<ErrorDialog>(
context: context,
builder: (BuildContext context) {
return ErrorDialog(error);
});
});
},
);
}
final InvoiceEntity credit;
final CompanyEntity company;
final Function(InvoiceEntity) onChanged;
final Function(BuildContext) onSavePressed;
final Function(BuildContext) onCancelPressed;
final bool isLoading;
final bool isSaving;
final InvoiceEntity origCredit;
final AppState state;
}

View File

@ -0,0 +1,51 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/ui/app/buttons/edit_icon_button.dart';
import 'package:invoiceninja_flutter/ui/app/actions_menu_button.dart';
import 'package:invoiceninja_flutter/ui/credit/view/credit_view_vm.dart';
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_state_title.dart';
class CreditView extends StatefulWidget {
const CreditView({
Key key,
@required this.viewModel,
}) : super(key: key);
final CreditViewVM viewModel;
@override
_CreditViewState createState() => new _CreditViewState();
}
class _CreditViewState extends State<CreditView> {
@override
Widget build(BuildContext context) {
final viewModel = widget.viewModel;
final userCompany = viewModel.state.userCompany;
final credit = viewModel.credit;
return Scaffold(
appBar: AppBar(
title: EntityStateTitle(entity: credit),
actions: [
userCompany.canEditEntity(credit)
? EditIconButton(
isVisible: !credit.isDeleted,
onPressed: () => viewModel.onEditPressed(context),
)
: Container(),
ActionMenuButton(
entityActions: credit.getActions(userCompany: userCompany),
isSaving: viewModel.isSaving,
entity: credit,
onSelected: viewModel.onEntityAction,
)
],
),
body: FormCard(children: [
// STARTER: widgets - do not remove comment
]),
);
}
}

View File

@ -0,0 +1,81 @@
import 'dart:async';
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/ui/credit/credit_screen.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:redux/redux.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
import 'package:invoiceninja_flutter/data/models/credit_model.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/ui/credit/view/credit_view.dart';
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
class CreditViewScreen extends StatelessWidget {
const CreditViewScreen({Key key}) : super(key: key);
static const String route = '/credit/view';
@override
Widget build(BuildContext context) {
return StoreConnector<AppState, CreditViewVM>(
converter: (Store<AppState> store) {
return CreditViewVM.fromStore(store);
},
builder: (context, vm) {
return CreditView(
viewModel: vm,
);
},
);
}
}
class CreditViewVM {
CreditViewVM({
@required this.state,
@required this.credit,
@required this.company,
@required this.onEntityAction,
@required this.onRefreshed,
@required this.isSaving,
@required this.isLoading,
@required this.isDirty,
});
factory CreditViewVM.fromStore(Store<AppState> store) {
final state = store.state;
final credit = state.creditState.map[state.creditUIState.selectedId] ??
InvoiceEntity(id: state.creditUIState.selectedId);
Future<Null> _handleRefresh(BuildContext context) {
final completer = snackBarCompleter<Null>(
context, AppLocalization.of(context).refreshComplete);
store.dispatch(LoadCredit(completer: completer, creditId: credit.id));
return completer.future;
}
return CreditViewVM(
state: state,
company: state.selectedCompany,
isSaving: state.isSaving,
isLoading: state.isLoading,
isDirty: credit.isNew,
credit: credit,
onRefreshed: (context) => _handleRefresh(context),
onEntityAction: (BuildContext context, EntityAction action) =>
handleCreditAction(context, credit, action),
);
}
final AppState state;
final InvoiceEntity credit;
final CompanyEntity company;
final Function(BuildContext, EntityAction) onEntityAction;
final Function(BuildContext) onRefreshed;
final bool isSaving;
final bool isLoading;
final bool isDirty;
}

View File

@ -24,7 +24,7 @@ enum CreditReportFields {
var memoizedCreditReport = memo6((
UserCompanyEntity userCompany,
ReportsUIState reportsUIState,
BuiltMap<String, CreditEntity> creditMap,
BuiltMap<String, InvoiceEntity> creditMap,
BuiltMap<String, ClientEntity> clientMap,
BuiltMap<String, UserEntity> userMap,
StaticState staticState,
@ -35,7 +35,7 @@ var memoizedCreditReport = memo6((
ReportResult creditReport(
UserCompanyEntity userCompany,
ReportsUIState reportsUIState,
BuiltMap<String, CreditEntity> creditMap,
BuiltMap<String, InvoiceEntity> creditMap,
BuiltMap<String, ClientEntity> clientMap,
BuiltMap<String, UserEntity> userMap,
StaticState staticState,

View File

@ -15,6 +15,17 @@ mixin LocalizationsProvider on LocaleCodeAware {
static final Map<String, Map<String, String>> _localizedValues = {
'en': {
// STARTER: lang key - do not remove comment
'credit': 'Credit',
'credits': 'Credits',
'new_credit': 'New Credit',
'edit_credit': 'Edit Credit',
'created_credit': 'Successfully created credit',
'updated_credit': 'Successfully updated credit',
'archived_credit': 'Successfully archived credit',
'deleted_credit': 'Successfully deleted credit',
'removed_credit': 'Successfully removed credit',
'restored_credit': 'Successfully restored credit',
'current_version': 'Current Version',
'latest_version': 'Latest Version',
'update_now': 'Update Now',
@ -32754,6 +32765,15 @@ mixin LocalizationsProvider on LocaleCodeAware {
String get appUpdated => _localizedValues[localeCode]['app_updated'];
// STARTER: lang field - do not remove comment
String get credit => _localizedValues[localeCode][' credit'];
String get credits => _localizedValues[localeCode][' credits'];
String get newCredit => _localizedValues[localeCode]['new_ credit'];
String get createdCredit => _localizedValues[localeCode]['created_ credit'];
String get updatedCredit => _localizedValues[localeCode]['updated_ credit'];
String get archivedCredit => _localizedValues[localeCode]['archived_ credit'];
String get deletedCredit => _localizedValues[localeCode]['deleted_ credit'];
String get restoredCredit => _localizedValues[localeCode]['restored_ credit'];
String get editCredit => _localizedValues[localeCode]['edit_ credit'];
String lookup(String key) {
final lookupKey = toSnakeCase(key);

View File

@ -44,6 +44,22 @@ class StubRepository {
return stubResponse.data;
}
Future<List<StubEntity>> bulkAction(
Credentials credentials, List<String> ids, EntityAction action) async {
var url = credentials.url + '/stubs/bulk?';
if (action != null) {
url += '&action=' + action.toString();
}
final dynamic response = await webClient.post(url, credentials.token,
data: json.encode({'ids': ids}));
final StubListResponse stubResponse =
serializers.deserializeWith(StubListResponse.serializer, response);
return stubResponse.data.toList();
}
Future<StubEntity> saveData(
Credentials credentials, StubEntity stub,
[EntityAction action]) async {

View File

@ -149,66 +149,64 @@ class SaveStubFailure implements StopSaving {
final Object error;
}
class ArchiveStubRequest implements StartSaving {
ArchiveStubRequest(this.completer, this.stubId);
class ArchiveStubsRequest implements StartSaving {
ArchiveStubsRequest(this.completer, this.stubIds);
final Completer completer;
final String stubId;
final List<String> stubIds;
}
class ArchiveStubSuccess implements StopSaving, PersistData {
ArchiveStubSuccess(this.stub);
class ArchiveStubsSuccess implements StopSaving, PersistData {
ArchiveStubsSuccess(this.stubs);
final StubEntity stub;
final List<StubEntity> stubs;
}
class ArchiveStubFailure implements StopSaving {
ArchiveStubFailure(this.stub);
class ArchiveStubsFailure implements StopSaving {
ArchiveStubsFailure(this.stubs);
final StubEntity stub;
final List<StubEntity> stubs;
}
class DeleteStubRequest implements StartSaving {
DeleteStubRequest(this.completer, this.stubId);
class DeleteStubsRequest implements StartSaving {
DeleteStubsRequest(this.completer, this.stubIds);
final Completer completer;
final String stubId;
final List<String> stubIds;
}
class DeleteStubSuccess implements StopSaving, PersistData {
DeleteStubSuccess(this.stub);
class DeleteStubsSuccess implements StopSaving, PersistData {
DeleteStubsSuccess(this.stubs);
final StubEntity stub;
final List<StubEntity> stubs;
}
class DeleteStubFailure implements StopSaving {
DeleteStubFailure(this.stub);
class DeleteStubsFailure implements StopSaving {
DeleteStubsFailure(this.stubs);
final StubEntity stub;
final List<StubEntity> stubs;
}
class RestoreStubRequest implements StartSaving {
RestoreStubRequest(this.completer, this.stubId);
class RestoreStubsRequest implements StartSaving {
RestoreStubsRequest(this.completer, this.stubIds);
final Completer completer;
final String stubId;
final List<String> stubIds;
}
class RestoreStubSuccess implements StopSaving, PersistData {
RestoreStubSuccess(this.stub);
class RestoreStubsSuccess implements StopSaving, PersistData {
RestoreStubsSuccess(this.stub);
final StubEntity stub;
final List<StubEntity> stubs;
}
class RestoreStubFailure implements StopSaving {
RestoreStubFailure(this.stub);
class RestoreStubsFailure implements StopSaving {
RestoreStubsFailure(this.stub);
final StubEntity stub;
final List<StubEntity> stubs;
}
class FilterStubs implements PersistUI {
FilterStubs(this.filter);
@ -268,25 +266,26 @@ void handleStubAction(
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final CompanyEntity company = state.selectedCompany;
final CompanyEntity company = state.company;
final localization = AppLocalization.of(context);
final stub = stubs.first as StubEntity;
final stubIds = stubs.map((stub) => stub.id).toList();
switch (action) {
case EntityAction.edit:
store.dispatch(EditStub(context: context, stub: stub));
editEntity(context: context, entity: stub);
break;
case EntityAction.restore:
store.dispatch(RestoreStubRequest(
snackBarCompleter<Null>(context, localization.restoredStub), stub.id));
store.dispatch(RestoreStubsRequest(
snackBarCompleter<Null>(context, localization.restoredStub), stubIds));
break;
case EntityAction.archive:
store.dispatch(ArchiveStubRequest(
snackBarCompleter<Null>(context, localization.archivedStub), stub.id));
store.dispatch(ArchiveStubsRequest(
snackBarCompleter<Null>(context, localization.archivedStub), stubIds));
break;
case EntityAction.delete:
store.dispatch(DeleteStubRequest(
snackBarCompleter<Null>(context, localization.deletedStub), stub.id));
store.dispatch(DeleteStubsRequest(
snackBarCompleter<Null>(context, localization.deletedStub), stubIds));
break;
case EntityAction.toggleMultiselect:
if (!store.state.stubListState.isInMultiselect()) {

View File

@ -53,7 +53,7 @@ Middleware<AppState> _editStub() {
store.dispatch(UpdateCurrentRoute(StubEditScreen.route));
if (isMobile(action.context)) {
action.navigator.pushNamed(QuoteEditScreen.route);
action.navigator.pushNamed(StubEditScreen.route);
}
};
}
@ -105,19 +105,20 @@ Middleware<AppState> _viewStubList() {
Middleware<AppState> _archiveStub(StubRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as ArchiveStubRequest;
final origStub = store.state.stubState.map[action.stubId];
final action = dynamicAction as ArchiveStubsRequest;
final prevStubs =
action.stubIds.map((id) => store.state.stubState.map[id]).toList();
repository
.saveData(store.state.credentials,
origStub, EntityAction.archive)
.then((StubEntity stub) {
store.dispatch(ArchiveStubSuccess(stub));
.bulkAction(
store.state.credentials, action.stubIds, EntityAction.archive)
.then((List<StubEntity> stubs) {
store.dispatch(ArchiveStubsSuccess(stubs));
if (action.completer != null) {
action.completer.complete(null);
}
}).catchError((Object error) {
print(error);
store.dispatch(ArchiveStubFailure(origStub));
store.dispatch(ArchiveStubsFailure(prevStubs));
if (action.completer != null) {
action.completer.completeError(error);
}
@ -129,19 +130,20 @@ Middleware<AppState> _archiveStub(StubRepository repository) {
Middleware<AppState> _deleteStub(StubRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as DeleteStubRequest;
final origStub = store.state.stubState.map[action.stubId];
final action = dynamicAction as DeleteStubsRequest;
final prevStubs =
action.stubIds.map((id) => store.state.stubState.map[id]).toList();
repository
.saveData(store.state.credentials,
origStub, EntityAction.delete)
.then((StubEntity stub) {
store.dispatch(DeleteStubSuccess(stub));
.bulkAction(
store.state.credentials, action.stubIds, EntityAction.delete)
.then((List<StubEntity> stubs) {
store.dispatch(DeleteStubsSuccess(stubs));
if (action.completer != null) {
action.completer.complete(null);
}
}).catchError((Object error) {
print(error);
store.dispatch(DeleteStubFailure(origStub));
store.dispatch(DeleteStubsFailure(prevStubs));
if (action.completer != null) {
action.completer.completeError(error);
}
@ -153,19 +155,20 @@ Middleware<AppState> _deleteStub(StubRepository repository) {
Middleware<AppState> _restoreStub(StubRepository repository) {
return (Store<AppState> store, dynamic dynamicAction, NextDispatcher next) {
final action = dynamicAction as RestoreStubRequest;
final origStub = store.state.stubState.map[action.stubId];
final action = dynamicAction as RestoreStubsRequest;
final prevStubs =
action.stubIds.map((id) => store.state.stubState.map[id]).toList();
repository
.saveData(store.state.credentials,
origStub, EntityAction.restore)
.then((StubEntity stub) {
store.dispatch(RestoreStubSuccess(stub));
.bulkAction(
store.state.credentials, action.stubIds, EntityAction.restore)
.then((List<StubEntity> stubs) {
store.dispatch(RestoreStubSuccess(stubs));
if (action.completer != null) {
action.completer.complete(null);
}
}).catchError((Object error) {
print(error);
store.dispatch(RestoreStubFailure(origStub));
store.dispatch(RestoreStubFailure(prevStubs));
if (action.completer != null) {
action.completer.completeError(error);
}

View File

@ -1,4 +1,5 @@
import 'package:redux/redux.dart';
import 'package:built_collection/built_collection.dart';
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
import 'package:invoiceninja_flutter/data/models/models.dart';
import 'package:invoiceninja_flutter/redux/company/company_actions.dart';
@ -118,7 +119,7 @@ ListUIState _sortStubs(ListUIState stubListState, SortStubs action) {
ListUIState _startListMultiselect(
ListUIState productListState, StartStubMultiselect action) {
return productListState.rebuild((b) => b..selectedIds = ListBuilder();
return productListState.rebuild((b) => b..selectedIds = ListBuilder());
}
ListUIState _addToListMultiselect(
@ -135,7 +136,7 @@ ListUIState _removeFromListMultiselect(
ListUIState _clearListMultiselect(
ListUIState productListState, ClearStubMultiselect action) {
return productListState.rebuild((b) => b..selectedIds = null;
return productListState.rebuild((b) => b..selectedIds = null);
}
final stubsReducer = combineReducers<StubState>([
@ -155,58 +156,104 @@ final stubsReducer = combineReducers<StubState>([
]);
StubState _archiveStubRequest(
StubState stubState, ArchiveStubRequest action) {
final stub = stubState.map[action.stubId]
.rebuild((b) => b..archivedAt = DateTime.now().millisecondsSinceEpoch);
StubState stubState, ArchiveStubsRequest action) {
final stubs = action.stubIds.map((id) => stubState.map[id]).toList();
return stubState.rebuild((b) => b..map[action.stubId] = stub);
for (int i = 0; i < stubs.length; i++) {
stubs[i] = stubs[i]
.rebuild((b) => b..archivedAt = DateTime.now().millisecondsSinceEpoch);
}
return stubState.rebuild((b) {
for (final stub in stubs) {
b.map[stub.id] = stub;
}
});
}
StubState _archiveStubSuccess(
StubState stubState, ArchiveStubSuccess action) {
return stubState.rebuild((b) => b..map[action.stub.id] = action.stub);
StubState stubState, ArchiveStubsSuccess action) {
return stubState.rebuild((b) {
for (final stub in action.stubs) {
b.map[stub.id] = stub;
}
});
}
StubState _archiveStubFailure(
StubState stubState, ArchiveStubFailure action) {
return stubState.rebuild((b) => b..map[action.stub.id] = action.stub);
StubState stubState, ArchiveStubsFailure action) {
return stubState.rebuild((b) {
for (final stub in action.stubs) {
b.map[stub.id] = stub;
}
});
}
StubState _deleteStubRequest(
StubState stubState, DeleteStubRequest action) {
final stub = stubState.map[action.stubId].rebuild((b) => b
..archivedAt = DateTime.now().millisecondsSinceEpoch
..isDeleted = true);
StubState stubState, DeleteStubsRequest action) {
final stubs = action.stubIds.map((id) => stubState.map[id]).toList();
return stubState.rebuild((b) => b..map[action.stubId] = stub);
for (int i = 0; i < stubs.length; i++) {
stubs[i] = stubs[i].rebuild((b) => b
..archivedAt = DateTime.now().millisecondsSinceEpoch
..isDeleted = true);
}
return stubState.rebuild((b) {
for (final stub in stubs) {
b.map[stub.id] = stub;
}
});
}
StubState _deleteStubSuccess(
StubState stubState, DeleteStubSuccess action) {
return stubState.rebuild((b) => b..map[action.stub.id] = action.stub);
StubState stubState, DeleteStubsSuccess action) {
return stubState.rebuild((b) {
for (final stub in action.stubs) {
b.map[stub.id] = stub;
}
});
}
StubState _deleteStubFailure(
StubState stubState, DeleteStubFailure action) {
return stubState.rebuild((b) => b..map[action.stub.id] = action.stub);
StubState stubState, DeleteStubsFailure action) {
return stubState.rebuild((b) {
for (final stub in action.stubs) {
b.map[stub.id] = stub;
}
});
}
StubState _restoreStubRequest(
StubState stubState, RestoreStubRequest action) {
final stub = stubState.map[action.stubId].rebuild((b) => b
..archivedAt = null
..isDeleted = false);
return stubState.rebuild((b) => b..map[action.stubId] = stub);
StubState stubState, RestoreStubsRequest action) {
final stubs = action.stubIds.map((id) => stubState.map[id]).toList();
for (int i = 0; i < stubs.length; i++) {
stubs[i] = stubs[i].rebuild((b) => b
..archivedAt = null
..isDeleted = false);
}
return stubState.rebuild((b) {
for (final stub in stubs) {
b.map[stub.id] = stub;
}
});
}
StubState _restoreStubSuccess(
StubState stubState, RestoreStubSuccess action) {
return stubState.rebuild((b) => b..map[action.stub.id] = action.stub);
return stubState.rebuild((b) {
for (final stub in action.stubs) {
b.map[stub.id] = stub;
}
});
}
StubState _restoreStubFailure(
StubState stubState, RestoreStubFailure action) {
return stubState.rebuild((b) => b..map[action.stub.id] = action.stub);
return stubState.rebuild((b) {
for (final stub in action.stubs) {
b.map[stub.id] = stub;
}
});
}
StubState _addStub(StubState stubState, AddStubSuccess action) {