diff --git a/lib/data/models/entities.dart b/lib/data/models/entities.dart index 1930c52ac..445381cd7 100644 --- a/lib/data/models/entities.dart +++ b/lib/data/models/entities.dart @@ -367,7 +367,7 @@ abstract class ActivityEntity implements Built { - factory QuoteListResponse([void updates(QuoteListResponseBuilder b)]) = - _$QuoteListResponse; - - QuoteListResponse._(); - - BuiltList get data; - - static Serializer get serializer => - _$quoteListResponseSerializer; -} - -abstract class QuoteItemResponse - implements Built { - factory QuoteItemResponse([void updates(QuoteItemResponseBuilder b)]) = - _$QuoteItemResponse; - - QuoteItemResponse._(); - - QuoteEntity get data; - - static Serializer get serializer => - _$quoteItemResponseSerializer; -} - -class QuoteFields { - static const String amount = 'amount'; - static const String clientId = 'clientId'; - static const String quoteStatusId = 'quoteStatusId'; - static const String quoteNumber = 'quoteNumber'; - static const String discount = 'discount'; - static const String poNumber = 'poNumber'; - static const String quoteDate = 'quoteDate'; - static const String validUntil = 'validUntil'; - static const String terms = 'terms'; - static const String partial = 'partial'; - static const String partialDueDate = 'partialDueDate'; - static const String publicNotes = 'publicNotes'; - static const String privateNotes = 'privateNotes'; - - static const String updatedAt = 'updatedAt'; - static const String archivedAt = 'archivedAt'; - static const String isDeleted = 'isDeleted'; -} - -abstract class QuoteEntity extends Object - with BaseEntity, CalculateInvoiceTotal - implements Built { - static int counter = 0; - - factory QuoteEntity() { - return _$QuoteEntity._( - id: --QuoteEntity.counter, - amount: 0.0, - clientId: 0, - quoteStatusId: 0, - quoteNumber: '', - discount: 0.0, - poNumber: '', - quoteDate: convertDateTimeToSqlDate(), - validUntil: '', - terms: '', - publicNotes: '', - privateNotes: '', - recurringQuoteId: 0, - taxName1: '', - taxRate1: 0.0, - taxName2: '', - taxRate2: 0.0, - isAmountDiscount: false, - footer: '', - partial: 0.0, - partialDueDate: '', - customValue1: 0.0, - customValue2: 0.0, - customTaxes1: false, - customTaxes2: false, - quoteInvoiceId: 0, - customTextValue1: '', - customTextValue2: '', - isPublic: false, - filename: '', - invoiceItems: BuiltList(), - invitations: BuiltList(), - updatedAt: 0, - archivedAt: 0, - isDeleted: false, - ); - } - - QuoteEntity._(); - - QuoteEntity get clone => rebuild((b) => b - ..id = --QuoteEntity.counter - ..quoteNumber = '' - ..isPublic = false); - - @override - EntityType get entityType { - return EntityType.invoice; - } - - double get amount; - - @BuiltValueField(wireName: 'client_id') - int get clientId; - - @BuiltValueField(wireName: 'invoice_status_id') - int get quoteStatusId; - - @BuiltValueField(wireName: 'invoice_number') - String get quoteNumber; - - @override - double get discount; - - @BuiltValueField(wireName: 'po_number') - String get poNumber; - - @BuiltValueField(wireName: 'invoice_date') - String get quoteDate; - - @BuiltValueField(wireName: 'due_date') - String get validUntil; - - String get terms; - - @BuiltValueField(wireName: 'public_notes') - String get publicNotes; - - @BuiltValueField(wireName: 'private_notes') - String get privateNotes; - - @BuiltValueField(wireName: 'recurring_invoice_id') - int get recurringQuoteId; - - @override - @BuiltValueField(wireName: 'tax_name1') - String get taxName1; - - @override - @BuiltValueField(wireName: 'tax_rate1') - double get taxRate1; - - @override - @BuiltValueField(wireName: 'tax_name2') - String get taxName2; - - @override - @BuiltValueField(wireName: 'tax_rate2') - double get taxRate2; - - @override - @BuiltValueField(wireName: 'is_amount_discount') - bool get isAmountDiscount; - - @BuiltValueField(wireName: 'invoice_footer') - String get footer; - - double get partial; - - @BuiltValueField(wireName: 'partial_due_date') - String get partialDueDate; - - @override - @BuiltValueField(wireName: 'custom_value1') - double get customValue1; - - @override - @BuiltValueField(wireName: 'custom_value2') - double get customValue2; - - @override - @BuiltValueField(wireName: 'custom_taxes1') - bool get customTaxes1; - - @override - @BuiltValueField(wireName: 'custom_taxes2') - bool get customTaxes2; - - @BuiltValueField(wireName: 'quote_invoice_id') - int get quoteInvoiceId; - - @BuiltValueField(wireName: 'custom_text_value1') - String get customTextValue1; - - @BuiltValueField(wireName: 'custom_text_value2') - String get customTextValue2; - - @BuiltValueField(wireName: 'is_public') - bool get isPublic; - - String get filename; - - @override - @BuiltValueField(wireName: 'invoice_items') - BuiltList get invoiceItems; - - BuiltList get invitations; - - //String get last_login; - //String get custom_messages; - - int compareTo(QuoteEntity quote, String sortField, bool sortAscending) { - int response = 0; - final QuoteEntity quoteA = sortAscending ? this : quote; - final QuoteEntity quoteB = sortAscending ? quote : this; - - switch (sortField) { - case QuoteFields.amount: - response = quoteA.amount.compareTo(quoteB.amount); - break; - case QuoteFields.updatedAt: - response = quoteA.updatedAt.compareTo(quoteB.updatedAt); - break; - case QuoteFields.quoteDate: - response = quoteA.quoteDate.compareTo(quoteB.quoteDate); - break; - } - - if (response == 0) { - return quoteA.quoteNumber.compareTo(quoteB.quoteNumber); - } else { - return response; - } - } - - @override - bool matchesStatuses(BuiltList statuses) { - if (statuses.isEmpty) { - return true; - } - - for (final status in statuses) { - if (status.id == quoteStatusId) { - return true; - } - - if (status.id == kInvoiceStatusPastDue && isPastDue) { - return true; - } - } - - return false; - } - - @override - bool matchesFilter(String filter) { - if (filter == null || filter.isEmpty) { - return true; - } - - if (quoteNumber.toLowerCase().contains(filter)) { - return true; - } else if (customTextValue1.isNotEmpty && - customTextValue1.toLowerCase().contains(filter)) { - return true; - } else if (customTextValue2.isNotEmpty && - customTextValue2.toLowerCase().contains(filter)) { - return true; - } - - return false; - } - - @override - String matchesFilterValue(String filter) { - if (filter == null || filter.isEmpty) { - return null; - } - - filter = filter.toLowerCase(); - if (customTextValue1.isNotEmpty && - customTextValue1.toLowerCase().contains(filter)) { - return customTextValue1; - } else if (customTextValue2.isNotEmpty && - customTextValue2.toLowerCase().contains(filter)) { - return customTextValue2; - } - return null; - } - - QuoteEntity applyTax(TaxRateEntity taxRate) { - QuoteEntity invoice = rebuild((b) => b - ..taxRate1 = taxRate.rate - ..taxName1 = taxRate.name); - - if (taxRate.isInclusive) { - invoice = invoice.rebuild((b) => b - ..invoiceItems.replace(invoiceItems - .map((item) => item.rebuild( - (b) => b.cost = round(b.cost / (100 + taxRate.rate) * 100, 2))) - .toList())); - } - - return invoice; - } - - @override - String get listDisplayName { - return quoteNumber; - } - - @override - double get listDisplayAmount => null; - - @override - FormatNumberType get listDisplayAmountType => FormatNumberType.money; - - double get requestedAmount => partial > 0 ? partial : amount; - - bool get isPastDue { - if (validUntil.isEmpty) { - return false; - } - - return !isDeleted && - isPublic && - quoteStatusId != kInvoiceStatusPaid && - DateTime.tryParse(validUntil) - .isBefore(DateTime.now().subtract(Duration(days: 1))); - } - - String get invitationLink => invitations.first?.link; - - String get invitationSilentLink => invitations.first?.silentLink; - - String get invitationDownloadLink => invitations.first?.downloadLink; - - static Serializer get serializer => _$quoteEntitySerializer; -} diff --git a/lib/data/models/quote_model.g.dart b/lib/data/models/quote_model.g.dart deleted file mode 100644 index 9f35ebb2a..000000000 --- a/lib/data/models/quote_model.g.dart +++ /dev/null @@ -1,1200 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'quote_model.dart'; - -// ************************************************************************** -// BuiltValueGenerator -// ************************************************************************** - -// ignore_for_file: always_put_control_body_on_new_line -// ignore_for_file: annotate_overrides -// ignore_for_file: avoid_annotating_with_dynamic -// ignore_for_file: avoid_catches_without_on_clauses -// ignore_for_file: avoid_returning_this -// ignore_for_file: lines_longer_than_80_chars -// ignore_for_file: omit_local_variable_types -// ignore_for_file: prefer_expression_function_bodies -// ignore_for_file: sort_constructors_first - -Serializer _$quoteListResponseSerializer = - new _$QuoteListResponseSerializer(); -Serializer _$quoteItemResponseSerializer = - new _$QuoteItemResponseSerializer(); -Serializer _$quoteEntitySerializer = new _$QuoteEntitySerializer(); - -class _$QuoteListResponseSerializer - implements StructuredSerializer { - @override - final Iterable types = const [QuoteListResponse, _$QuoteListResponse]; - @override - final String wireName = 'QuoteListResponse'; - - @override - Iterable serialize(Serializers serializers, QuoteListResponse object, - {FullType specifiedType = FullType.unspecified}) { - final result = [ - 'data', - serializers.serialize(object.data, - specifiedType: - const FullType(BuiltList, const [const FullType(QuoteEntity)])), - ]; - - return result; - } - - @override - QuoteListResponse deserialize(Serializers serializers, Iterable serialized, - {FullType specifiedType = FullType.unspecified}) { - final result = new QuoteListResponseBuilder(); - - final iterator = serialized.iterator; - while (iterator.moveNext()) { - final key = iterator.current as String; - iterator.moveNext(); - final dynamic value = iterator.current; - switch (key) { - case 'data': - result.data.replace(serializers.deserialize(value, - specifiedType: const FullType( - BuiltList, const [const FullType(QuoteEntity)])) - as BuiltList); - break; - } - } - - return result.build(); - } -} - -class _$QuoteItemResponseSerializer - implements StructuredSerializer { - @override - final Iterable types = const [QuoteItemResponse, _$QuoteItemResponse]; - @override - final String wireName = 'QuoteItemResponse'; - - @override - Iterable serialize(Serializers serializers, QuoteItemResponse object, - {FullType specifiedType = FullType.unspecified}) { - final result = [ - 'data', - serializers.serialize(object.data, - specifiedType: const FullType(QuoteEntity)), - ]; - - return result; - } - - @override - QuoteItemResponse deserialize(Serializers serializers, Iterable serialized, - {FullType specifiedType = FullType.unspecified}) { - final result = new QuoteItemResponseBuilder(); - - final iterator = serialized.iterator; - while (iterator.moveNext()) { - final key = iterator.current as String; - iterator.moveNext(); - final dynamic value = iterator.current; - switch (key) { - case 'data': - result.data.replace(serializers.deserialize(value, - specifiedType: const FullType(QuoteEntity)) as QuoteEntity); - break; - } - } - - return result.build(); - } -} - -class _$QuoteEntitySerializer implements StructuredSerializer { - @override - final Iterable types = const [QuoteEntity, _$QuoteEntity]; - @override - final String wireName = 'QuoteEntity'; - - @override - Iterable serialize(Serializers serializers, QuoteEntity object, - {FullType specifiedType = FullType.unspecified}) { - final result = [ - 'amount', - serializers.serialize(object.amount, - specifiedType: const FullType(double)), - 'client_id', - serializers.serialize(object.clientId, - specifiedType: const FullType(int)), - 'invoice_status_id', - serializers.serialize(object.quoteStatusId, - specifiedType: const FullType(int)), - 'invoice_number', - serializers.serialize(object.quoteNumber, - specifiedType: const FullType(String)), - 'discount', - serializers.serialize(object.discount, - specifiedType: const FullType(double)), - 'po_number', - serializers.serialize(object.poNumber, - specifiedType: const FullType(String)), - 'invoice_date', - serializers.serialize(object.quoteDate, - specifiedType: const FullType(String)), - 'due_date', - serializers.serialize(object.validUntil, - specifiedType: const FullType(String)), - 'terms', - serializers.serialize(object.terms, - specifiedType: const FullType(String)), - 'public_notes', - serializers.serialize(object.publicNotes, - specifiedType: const FullType(String)), - 'private_notes', - serializers.serialize(object.privateNotes, - specifiedType: const FullType(String)), - 'recurring_invoice_id', - serializers.serialize(object.recurringQuoteId, - specifiedType: const FullType(int)), - 'tax_name1', - serializers.serialize(object.taxName1, - specifiedType: const FullType(String)), - 'tax_rate1', - serializers.serialize(object.taxRate1, - specifiedType: const FullType(double)), - 'tax_name2', - serializers.serialize(object.taxName2, - specifiedType: const FullType(String)), - 'tax_rate2', - serializers.serialize(object.taxRate2, - specifiedType: const FullType(double)), - 'is_amount_discount', - serializers.serialize(object.isAmountDiscount, - specifiedType: const FullType(bool)), - 'invoice_footer', - serializers.serialize(object.footer, - specifiedType: const FullType(String)), - 'partial', - serializers.serialize(object.partial, - specifiedType: const FullType(double)), - 'partial_due_date', - serializers.serialize(object.partialDueDate, - specifiedType: const FullType(String)), - 'custom_value1', - serializers.serialize(object.customValue1, - specifiedType: const FullType(double)), - 'custom_value2', - serializers.serialize(object.customValue2, - specifiedType: const FullType(double)), - 'custom_taxes1', - serializers.serialize(object.customTaxes1, - specifiedType: const FullType(bool)), - 'custom_taxes2', - serializers.serialize(object.customTaxes2, - specifiedType: const FullType(bool)), - 'quote_invoice_id', - serializers.serialize(object.quoteInvoiceId, - specifiedType: const FullType(int)), - 'custom_text_value1', - serializers.serialize(object.customTextValue1, - specifiedType: const FullType(String)), - 'custom_text_value2', - serializers.serialize(object.customTextValue2, - specifiedType: const FullType(String)), - 'is_public', - serializers.serialize(object.isPublic, - specifiedType: const FullType(bool)), - 'filename', - serializers.serialize(object.filename, - specifiedType: const FullType(String)), - 'invoice_items', - serializers.serialize(object.invoiceItems, - specifiedType: const FullType( - BuiltList, const [const FullType(InvoiceItemEntity)])), - 'invitations', - serializers.serialize(object.invitations, - specifiedType: const FullType( - BuiltList, const [const FullType(InvitationEntity)])), - ]; - 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.isOwner != null) { - result - ..add('is_owner') - ..add(serializers.serialize(object.isOwner, - specifiedType: const FullType(bool))); - } - if (object.id != null) { - result - ..add('id') - ..add(serializers.serialize(object.id, - specifiedType: const FullType(int))); - } - - return result; - } - - @override - QuoteEntity deserialize(Serializers serializers, Iterable serialized, - {FullType specifiedType = FullType.unspecified}) { - final result = new QuoteEntityBuilder(); - - 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 'client_id': - result.clientId = serializers.deserialize(value, - specifiedType: const FullType(int)) as int; - break; - case 'invoice_status_id': - result.quoteStatusId = serializers.deserialize(value, - specifiedType: const FullType(int)) as int; - break; - case 'invoice_number': - result.quoteNumber = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'discount': - result.discount = serializers.deserialize(value, - specifiedType: const FullType(double)) as double; - break; - case 'po_number': - result.poNumber = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'invoice_date': - result.quoteDate = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'due_date': - result.validUntil = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'terms': - result.terms = 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 'private_notes': - result.privateNotes = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'recurring_invoice_id': - result.recurringQuoteId = serializers.deserialize(value, - specifiedType: const FullType(int)) as int; - break; - case 'tax_name1': - result.taxName1 = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'tax_rate1': - result.taxRate1 = serializers.deserialize(value, - specifiedType: const FullType(double)) as double; - break; - case 'tax_name2': - result.taxName2 = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'tax_rate2': - result.taxRate2 = serializers.deserialize(value, - specifiedType: const FullType(double)) as double; - break; - case 'is_amount_discount': - result.isAmountDiscount = serializers.deserialize(value, - specifiedType: const FullType(bool)) as bool; - break; - case 'invoice_footer': - result.footer = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'partial': - result.partial = serializers.deserialize(value, - specifiedType: const FullType(double)) as double; - break; - case 'partial_due_date': - result.partialDueDate = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'custom_value1': - result.customValue1 = serializers.deserialize(value, - specifiedType: const FullType(double)) as double; - break; - case 'custom_value2': - result.customValue2 = serializers.deserialize(value, - specifiedType: const FullType(double)) as double; - break; - case 'custom_taxes1': - result.customTaxes1 = serializers.deserialize(value, - specifiedType: const FullType(bool)) as bool; - break; - case 'custom_taxes2': - result.customTaxes2 = serializers.deserialize(value, - specifiedType: const FullType(bool)) as bool; - break; - case 'quote_invoice_id': - result.quoteInvoiceId = serializers.deserialize(value, - specifiedType: const FullType(int)) as int; - break; - case 'custom_text_value1': - result.customTextValue1 = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'custom_text_value2': - result.customTextValue2 = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'is_public': - result.isPublic = serializers.deserialize(value, - specifiedType: const FullType(bool)) as bool; - break; - case 'filename': - result.filename = serializers.deserialize(value, - specifiedType: const FullType(String)) as String; - break; - case 'invoice_items': - result.invoiceItems.replace(serializers.deserialize(value, - specifiedType: const FullType( - BuiltList, const [const FullType(InvoiceItemEntity)])) - as BuiltList); - break; - case 'invitations': - result.invitations.replace(serializers.deserialize(value, - specifiedType: const FullType( - BuiltList, const [const FullType(InvitationEntity)])) - as BuiltList); - 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 'is_owner': - result.isOwner = serializers.deserialize(value, - specifiedType: const FullType(bool)) as bool; - break; - case 'id': - result.id = serializers.deserialize(value, - specifiedType: const FullType(int)) as int; - break; - } - } - - return result.build(); - } -} - -class _$QuoteListResponse extends QuoteListResponse { - @override - final BuiltList data; - - factory _$QuoteListResponse([void updates(QuoteListResponseBuilder b)]) => - (new QuoteListResponseBuilder()..update(updates)).build(); - - _$QuoteListResponse._({this.data}) : super._() { - if (data == null) - throw new BuiltValueNullFieldError('QuoteListResponse', 'data'); - } - - @override - QuoteListResponse rebuild(void updates(QuoteListResponseBuilder b)) => - (toBuilder()..update(updates)).build(); - - @override - QuoteListResponseBuilder toBuilder() => - new QuoteListResponseBuilder()..replace(this); - - @override - bool operator ==(dynamic other) { - if (identical(other, this)) return true; - if (other is! QuoteListResponse) return false; - return data == other.data; - } - - @override - int get hashCode { - return $jf($jc(0, data.hashCode)); - } - - @override - String toString() { - return (newBuiltValueToStringHelper('QuoteListResponse')..add('data', data)) - .toString(); - } -} - -class QuoteListResponseBuilder - implements Builder { - _$QuoteListResponse _$v; - - ListBuilder _data; - ListBuilder get data => - _$this._data ??= new ListBuilder(); - set data(ListBuilder data) => _$this._data = data; - - QuoteListResponseBuilder(); - - QuoteListResponseBuilder get _$this { - if (_$v != null) { - _data = _$v.data?.toBuilder(); - _$v = null; - } - return this; - } - - @override - void replace(QuoteListResponse other) { - if (other == null) throw new ArgumentError.notNull('other'); - _$v = other as _$QuoteListResponse; - } - - @override - void update(void updates(QuoteListResponseBuilder b)) { - if (updates != null) updates(this); - } - - @override - _$QuoteListResponse build() { - _$QuoteListResponse _$result; - try { - _$result = _$v ?? new _$QuoteListResponse._(data: data.build()); - } catch (_) { - String _$failedField; - try { - _$failedField = 'data'; - data.build(); - } catch (e) { - throw new BuiltValueNestedFieldError( - 'QuoteListResponse', _$failedField, e.toString()); - } - rethrow; - } - replace(_$result); - return _$result; - } -} - -class _$QuoteItemResponse extends QuoteItemResponse { - @override - final QuoteEntity data; - - factory _$QuoteItemResponse([void updates(QuoteItemResponseBuilder b)]) => - (new QuoteItemResponseBuilder()..update(updates)).build(); - - _$QuoteItemResponse._({this.data}) : super._() { - if (data == null) - throw new BuiltValueNullFieldError('QuoteItemResponse', 'data'); - } - - @override - QuoteItemResponse rebuild(void updates(QuoteItemResponseBuilder b)) => - (toBuilder()..update(updates)).build(); - - @override - QuoteItemResponseBuilder toBuilder() => - new QuoteItemResponseBuilder()..replace(this); - - @override - bool operator ==(dynamic other) { - if (identical(other, this)) return true; - if (other is! QuoteItemResponse) return false; - return data == other.data; - } - - @override - int get hashCode { - return $jf($jc(0, data.hashCode)); - } - - @override - String toString() { - return (newBuiltValueToStringHelper('QuoteItemResponse')..add('data', data)) - .toString(); - } -} - -class QuoteItemResponseBuilder - implements Builder { - _$QuoteItemResponse _$v; - - QuoteEntityBuilder _data; - QuoteEntityBuilder get data => _$this._data ??= new QuoteEntityBuilder(); - set data(QuoteEntityBuilder data) => _$this._data = data; - - QuoteItemResponseBuilder(); - - QuoteItemResponseBuilder get _$this { - if (_$v != null) { - _data = _$v.data?.toBuilder(); - _$v = null; - } - return this; - } - - @override - void replace(QuoteItemResponse other) { - if (other == null) throw new ArgumentError.notNull('other'); - _$v = other as _$QuoteItemResponse; - } - - @override - void update(void updates(QuoteItemResponseBuilder b)) { - if (updates != null) updates(this); - } - - @override - _$QuoteItemResponse build() { - _$QuoteItemResponse _$result; - try { - _$result = _$v ?? new _$QuoteItemResponse._(data: data.build()); - } catch (_) { - String _$failedField; - try { - _$failedField = 'data'; - data.build(); - } catch (e) { - throw new BuiltValueNestedFieldError( - 'QuoteItemResponse', _$failedField, e.toString()); - } - rethrow; - } - replace(_$result); - return _$result; - } -} - -class _$QuoteEntity extends QuoteEntity { - @override - final double amount; - @override - final int clientId; - @override - final int quoteStatusId; - @override - final String quoteNumber; - @override - final double discount; - @override - final String poNumber; - @override - final String quoteDate; - @override - final String validUntil; - @override - final String terms; - @override - final String publicNotes; - @override - final String privateNotes; - @override - final int recurringQuoteId; - @override - final String taxName1; - @override - final double taxRate1; - @override - final String taxName2; - @override - final double taxRate2; - @override - final bool isAmountDiscount; - @override - final String footer; - @override - final double partial; - @override - final String partialDueDate; - @override - final double customValue1; - @override - final double customValue2; - @override - final bool customTaxes1; - @override - final bool customTaxes2; - @override - final int quoteInvoiceId; - @override - final String customTextValue1; - @override - final String customTextValue2; - @override - final bool isPublic; - @override - final String filename; - @override - final BuiltList invoiceItems; - @override - final BuiltList invitations; - @override - final int createdAt; - @override - final int updatedAt; - @override - final int archivedAt; - @override - final bool isDeleted; - @override - final bool isOwner; - @override - final int id; - - factory _$QuoteEntity([void updates(QuoteEntityBuilder b)]) => - (new QuoteEntityBuilder()..update(updates)).build(); - - _$QuoteEntity._( - {this.amount, - this.clientId, - this.quoteStatusId, - this.quoteNumber, - this.discount, - this.poNumber, - this.quoteDate, - this.validUntil, - this.terms, - this.publicNotes, - this.privateNotes, - this.recurringQuoteId, - this.taxName1, - this.taxRate1, - this.taxName2, - this.taxRate2, - this.isAmountDiscount, - this.footer, - this.partial, - this.partialDueDate, - this.customValue1, - this.customValue2, - this.customTaxes1, - this.customTaxes2, - this.quoteInvoiceId, - this.customTextValue1, - this.customTextValue2, - this.isPublic, - this.filename, - this.invoiceItems, - this.invitations, - this.createdAt, - this.updatedAt, - this.archivedAt, - this.isDeleted, - this.isOwner, - this.id}) - : super._() { - if (amount == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'amount'); - if (clientId == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'clientId'); - if (quoteStatusId == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'quoteStatusId'); - if (quoteNumber == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'quoteNumber'); - if (discount == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'discount'); - if (poNumber == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'poNumber'); - if (quoteDate == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'quoteDate'); - if (validUntil == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'validUntil'); - if (terms == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'terms'); - if (publicNotes == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'publicNotes'); - if (privateNotes == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'privateNotes'); - if (recurringQuoteId == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'recurringQuoteId'); - if (taxName1 == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'taxName1'); - if (taxRate1 == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'taxRate1'); - if (taxName2 == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'taxName2'); - if (taxRate2 == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'taxRate2'); - if (isAmountDiscount == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'isAmountDiscount'); - if (footer == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'footer'); - if (partial == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'partial'); - if (partialDueDate == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'partialDueDate'); - if (customValue1 == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'customValue1'); - if (customValue2 == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'customValue2'); - if (customTaxes1 == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'customTaxes1'); - if (customTaxes2 == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'customTaxes2'); - if (quoteInvoiceId == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'quoteInvoiceId'); - if (customTextValue1 == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'customTextValue1'); - if (customTextValue2 == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'customTextValue2'); - if (isPublic == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'isPublic'); - if (filename == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'filename'); - if (invoiceItems == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'invoiceItems'); - if (invitations == null) - throw new BuiltValueNullFieldError('QuoteEntity', 'invitations'); - } - - @override - QuoteEntity rebuild(void updates(QuoteEntityBuilder b)) => - (toBuilder()..update(updates)).build(); - - @override - QuoteEntityBuilder toBuilder() => new QuoteEntityBuilder()..replace(this); - - @override - bool operator ==(dynamic other) { - if (identical(other, this)) return true; - if (other is! QuoteEntity) return false; - return amount == other.amount && - clientId == other.clientId && - quoteStatusId == other.quoteStatusId && - quoteNumber == other.quoteNumber && - discount == other.discount && - poNumber == other.poNumber && - quoteDate == other.quoteDate && - validUntil == other.validUntil && - terms == other.terms && - publicNotes == other.publicNotes && - privateNotes == other.privateNotes && - recurringQuoteId == other.recurringQuoteId && - taxName1 == other.taxName1 && - taxRate1 == other.taxRate1 && - taxName2 == other.taxName2 && - taxRate2 == other.taxRate2 && - isAmountDiscount == other.isAmountDiscount && - footer == other.footer && - partial == other.partial && - partialDueDate == other.partialDueDate && - customValue1 == other.customValue1 && - customValue2 == other.customValue2 && - customTaxes1 == other.customTaxes1 && - customTaxes2 == other.customTaxes2 && - quoteInvoiceId == other.quoteInvoiceId && - customTextValue1 == other.customTextValue1 && - customTextValue2 == other.customTextValue2 && - isPublic == other.isPublic && - filename == other.filename && - invoiceItems == other.invoiceItems && - invitations == other.invitations && - createdAt == other.createdAt && - updatedAt == other.updatedAt && - archivedAt == other.archivedAt && - isDeleted == other.isDeleted && - isOwner == other.isOwner && - 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( - $jc( - $jc( - $jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc(0, amount.hashCode), clientId.hashCode), quoteStatusId.hashCode), quoteNumber.hashCode), discount.hashCode), poNumber.hashCode), quoteDate.hashCode), validUntil.hashCode), terms.hashCode), publicNotes.hashCode), privateNotes.hashCode), recurringQuoteId.hashCode), taxName1.hashCode), taxRate1.hashCode), taxName2.hashCode), taxRate2.hashCode), isAmountDiscount.hashCode), footer.hashCode), - partial.hashCode), - partialDueDate.hashCode), - customValue1.hashCode), - customValue2.hashCode), - customTaxes1.hashCode), - customTaxes2.hashCode), - quoteInvoiceId.hashCode), - customTextValue1.hashCode), - customTextValue2.hashCode), - isPublic.hashCode), - filename.hashCode), - invoiceItems.hashCode), - invitations.hashCode), - createdAt.hashCode), - updatedAt.hashCode), - archivedAt.hashCode), - isDeleted.hashCode), - isOwner.hashCode), - id.hashCode)); - } - - @override - String toString() { - return (newBuiltValueToStringHelper('QuoteEntity') - ..add('amount', amount) - ..add('clientId', clientId) - ..add('quoteStatusId', quoteStatusId) - ..add('quoteNumber', quoteNumber) - ..add('discount', discount) - ..add('poNumber', poNumber) - ..add('quoteDate', quoteDate) - ..add('validUntil', validUntil) - ..add('terms', terms) - ..add('publicNotes', publicNotes) - ..add('privateNotes', privateNotes) - ..add('recurringQuoteId', recurringQuoteId) - ..add('taxName1', taxName1) - ..add('taxRate1', taxRate1) - ..add('taxName2', taxName2) - ..add('taxRate2', taxRate2) - ..add('isAmountDiscount', isAmountDiscount) - ..add('footer', footer) - ..add('partial', partial) - ..add('partialDueDate', partialDueDate) - ..add('customValue1', customValue1) - ..add('customValue2', customValue2) - ..add('customTaxes1', customTaxes1) - ..add('customTaxes2', customTaxes2) - ..add('quoteInvoiceId', quoteInvoiceId) - ..add('customTextValue1', customTextValue1) - ..add('customTextValue2', customTextValue2) - ..add('isPublic', isPublic) - ..add('filename', filename) - ..add('invoiceItems', invoiceItems) - ..add('invitations', invitations) - ..add('createdAt', createdAt) - ..add('updatedAt', updatedAt) - ..add('archivedAt', archivedAt) - ..add('isDeleted', isDeleted) - ..add('isOwner', isOwner) - ..add('id', id)) - .toString(); - } -} - -class QuoteEntityBuilder implements Builder { - _$QuoteEntity _$v; - - double _amount; - double get amount => _$this._amount; - set amount(double amount) => _$this._amount = amount; - - int _clientId; - int get clientId => _$this._clientId; - set clientId(int clientId) => _$this._clientId = clientId; - - int _quoteStatusId; - int get quoteStatusId => _$this._quoteStatusId; - set quoteStatusId(int quoteStatusId) => _$this._quoteStatusId = quoteStatusId; - - String _quoteNumber; - String get quoteNumber => _$this._quoteNumber; - set quoteNumber(String quoteNumber) => _$this._quoteNumber = quoteNumber; - - double _discount; - double get discount => _$this._discount; - set discount(double discount) => _$this._discount = discount; - - String _poNumber; - String get poNumber => _$this._poNumber; - set poNumber(String poNumber) => _$this._poNumber = poNumber; - - String _quoteDate; - String get quoteDate => _$this._quoteDate; - set quoteDate(String quoteDate) => _$this._quoteDate = quoteDate; - - String _validUntil; - String get validUntil => _$this._validUntil; - set validUntil(String validUntil) => _$this._validUntil = validUntil; - - String _terms; - String get terms => _$this._terms; - set terms(String terms) => _$this._terms = terms; - - String _publicNotes; - String get publicNotes => _$this._publicNotes; - set publicNotes(String publicNotes) => _$this._publicNotes = publicNotes; - - String _privateNotes; - String get privateNotes => _$this._privateNotes; - set privateNotes(String privateNotes) => _$this._privateNotes = privateNotes; - - int _recurringQuoteId; - int get recurringQuoteId => _$this._recurringQuoteId; - set recurringQuoteId(int recurringQuoteId) => - _$this._recurringQuoteId = recurringQuoteId; - - String _taxName1; - String get taxName1 => _$this._taxName1; - set taxName1(String taxName1) => _$this._taxName1 = taxName1; - - double _taxRate1; - double get taxRate1 => _$this._taxRate1; - set taxRate1(double taxRate1) => _$this._taxRate1 = taxRate1; - - String _taxName2; - String get taxName2 => _$this._taxName2; - set taxName2(String taxName2) => _$this._taxName2 = taxName2; - - double _taxRate2; - double get taxRate2 => _$this._taxRate2; - set taxRate2(double taxRate2) => _$this._taxRate2 = taxRate2; - - bool _isAmountDiscount; - bool get isAmountDiscount => _$this._isAmountDiscount; - set isAmountDiscount(bool isAmountDiscount) => - _$this._isAmountDiscount = isAmountDiscount; - - String _footer; - String get footer => _$this._footer; - set footer(String footer) => _$this._footer = footer; - - double _partial; - double get partial => _$this._partial; - set partial(double partial) => _$this._partial = partial; - - String _partialDueDate; - String get partialDueDate => _$this._partialDueDate; - set partialDueDate(String partialDueDate) => - _$this._partialDueDate = partialDueDate; - - double _customValue1; - double get customValue1 => _$this._customValue1; - set customValue1(double customValue1) => _$this._customValue1 = customValue1; - - double _customValue2; - double get customValue2 => _$this._customValue2; - set customValue2(double customValue2) => _$this._customValue2 = customValue2; - - bool _customTaxes1; - bool get customTaxes1 => _$this._customTaxes1; - set customTaxes1(bool customTaxes1) => _$this._customTaxes1 = customTaxes1; - - bool _customTaxes2; - bool get customTaxes2 => _$this._customTaxes2; - set customTaxes2(bool customTaxes2) => _$this._customTaxes2 = customTaxes2; - - int _quoteInvoiceId; - int get quoteInvoiceId => _$this._quoteInvoiceId; - set quoteInvoiceId(int quoteInvoiceId) => - _$this._quoteInvoiceId = quoteInvoiceId; - - String _customTextValue1; - String get customTextValue1 => _$this._customTextValue1; - set customTextValue1(String customTextValue1) => - _$this._customTextValue1 = customTextValue1; - - String _customTextValue2; - String get customTextValue2 => _$this._customTextValue2; - set customTextValue2(String customTextValue2) => - _$this._customTextValue2 = customTextValue2; - - bool _isPublic; - bool get isPublic => _$this._isPublic; - set isPublic(bool isPublic) => _$this._isPublic = isPublic; - - String _filename; - String get filename => _$this._filename; - set filename(String filename) => _$this._filename = filename; - - ListBuilder _invoiceItems; - ListBuilder get invoiceItems => - _$this._invoiceItems ??= new ListBuilder(); - set invoiceItems(ListBuilder invoiceItems) => - _$this._invoiceItems = invoiceItems; - - ListBuilder _invitations; - ListBuilder get invitations => - _$this._invitations ??= new ListBuilder(); - set invitations(ListBuilder invitations) => - _$this._invitations = invitations; - - 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; - - bool _isOwner; - bool get isOwner => _$this._isOwner; - set isOwner(bool isOwner) => _$this._isOwner = isOwner; - - int _id; - int get id => _$this._id; - set id(int id) => _$this._id = id; - - QuoteEntityBuilder(); - - QuoteEntityBuilder get _$this { - if (_$v != null) { - _amount = _$v.amount; - _clientId = _$v.clientId; - _quoteStatusId = _$v.quoteStatusId; - _quoteNumber = _$v.quoteNumber; - _discount = _$v.discount; - _poNumber = _$v.poNumber; - _quoteDate = _$v.quoteDate; - _validUntil = _$v.validUntil; - _terms = _$v.terms; - _publicNotes = _$v.publicNotes; - _privateNotes = _$v.privateNotes; - _recurringQuoteId = _$v.recurringQuoteId; - _taxName1 = _$v.taxName1; - _taxRate1 = _$v.taxRate1; - _taxName2 = _$v.taxName2; - _taxRate2 = _$v.taxRate2; - _isAmountDiscount = _$v.isAmountDiscount; - _footer = _$v.footer; - _partial = _$v.partial; - _partialDueDate = _$v.partialDueDate; - _customValue1 = _$v.customValue1; - _customValue2 = _$v.customValue2; - _customTaxes1 = _$v.customTaxes1; - _customTaxes2 = _$v.customTaxes2; - _quoteInvoiceId = _$v.quoteInvoiceId; - _customTextValue1 = _$v.customTextValue1; - _customTextValue2 = _$v.customTextValue2; - _isPublic = _$v.isPublic; - _filename = _$v.filename; - _invoiceItems = _$v.invoiceItems?.toBuilder(); - _invitations = _$v.invitations?.toBuilder(); - _createdAt = _$v.createdAt; - _updatedAt = _$v.updatedAt; - _archivedAt = _$v.archivedAt; - _isDeleted = _$v.isDeleted; - _isOwner = _$v.isOwner; - _id = _$v.id; - _$v = null; - } - return this; - } - - @override - void replace(QuoteEntity other) { - if (other == null) throw new ArgumentError.notNull('other'); - _$v = other as _$QuoteEntity; - } - - @override - void update(void updates(QuoteEntityBuilder b)) { - if (updates != null) updates(this); - } - - @override - _$QuoteEntity build() { - _$QuoteEntity _$result; - try { - _$result = _$v ?? - new _$QuoteEntity._( - amount: amount, - clientId: clientId, - quoteStatusId: quoteStatusId, - quoteNumber: quoteNumber, - discount: discount, - poNumber: poNumber, - quoteDate: quoteDate, - validUntil: validUntil, - terms: terms, - publicNotes: publicNotes, - privateNotes: privateNotes, - recurringQuoteId: recurringQuoteId, - taxName1: taxName1, - taxRate1: taxRate1, - taxName2: taxName2, - taxRate2: taxRate2, - isAmountDiscount: isAmountDiscount, - footer: footer, - partial: partial, - partialDueDate: partialDueDate, - customValue1: customValue1, - customValue2: customValue2, - customTaxes1: customTaxes1, - customTaxes2: customTaxes2, - quoteInvoiceId: quoteInvoiceId, - customTextValue1: customTextValue1, - customTextValue2: customTextValue2, - isPublic: isPublic, - filename: filename, - invoiceItems: invoiceItems.build(), - invitations: invitations.build(), - createdAt: createdAt, - updatedAt: updatedAt, - archivedAt: archivedAt, - isDeleted: isDeleted, - isOwner: isOwner, - id: id); - } catch (_) { - String _$failedField; - try { - _$failedField = 'invoiceItems'; - invoiceItems.build(); - _$failedField = 'invitations'; - invitations.build(); - } catch (e) { - throw new BuiltValueNestedFieldError( - 'QuoteEntity', _$failedField, e.toString()); - } - rethrow; - } - replace(_$result); - return _$result; - } -} diff --git a/lib/data/models/serializers.dart b/lib/data/models/serializers.dart index 3387ec894..ffb435331 100644 --- a/lib/data/models/serializers.dart +++ b/lib/data/models/serializers.dart @@ -21,7 +21,6 @@ import 'package:invoiceninja_flutter/redux/client/client_state.dart'; import 'package:invoiceninja_flutter/redux/ui/ui_state.dart'; import 'package:invoiceninja_flutter/redux/invoice/invoice_state.dart'; // STARTER: import - do not remove comment -import 'package:invoiceninja_flutter/data/models/quote_model.dart'; import 'package:invoiceninja_flutter/redux/quote/quote_state.dart'; @@ -73,7 +72,6 @@ part 'serializers.g.dart'; TimezoneItemResponse, TimezoneListResponse, // STARTER: serializers - do not remove comment -QuoteEntity, ]) final Serializers serializers = diff --git a/lib/data/models/serializers.g.dart b/lib/data/models/serializers.g.dart index 95aa5941e..2640084dd 100644 --- a/lib/data/models/serializers.g.dart +++ b/lib/data/models/serializers.g.dart @@ -91,7 +91,7 @@ Serializers _$serializers = (new Serializers().toBuilder() ..add(ProjectEntity.serializer) ..add(ProjectItemResponse.serializer) ..add(ProjectListResponse.serializer) - ..add(QuoteEntity.serializer) + ..add(InvoiceEntity.serializer) ..add(QuoteState.serializer) ..add(QuoteUIState.serializer) ..add(SizeEntity.serializer) @@ -344,6 +344,6 @@ Serializers _$serializers = (new Serializers().toBuilder() ..addBuilderFactory(const FullType(BuiltList, const [const FullType(int)]), () => new ListBuilder()) ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(int), const FullType(ProductEntity)]), () => new MapBuilder()) ..addBuilderFactory(const FullType(BuiltList, const [const FullType(int)]), () => new ListBuilder()) - ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(int), const FullType(QuoteEntity)]), () => new MapBuilder()) + ..addBuilderFactory(const FullType(BuiltMap, const [const FullType(int), const FullType(InvoiceEntity)]), () => new MapBuilder()) ..addBuilderFactory(const FullType(BuiltList, const [const FullType(int)]), () => new ListBuilder())) .build(); diff --git a/lib/data/repositories/quote_repository.dart b/lib/data/repositories/quote_repository.dart index 79750a9ec..e568901c0 100644 --- a/lib/data/repositories/quote_repository.dart +++ b/lib/data/repositories/quote_repository.dart @@ -14,18 +14,18 @@ class QuoteRepository { this.webClient = const WebClient(), }); - Future loadItem( + Future loadItem( CompanyEntity company, AuthState auth, int entityId) async { final dynamic response = await webClient.get( - '${auth.url}/quotes/$entityId', company.token); + '${auth.url}/invoices/$entityId', company.token); - final QuoteItemResponse quoteResponse = - serializers.deserializeWith(QuoteItemResponse.serializer, response); + final InvoiceItemResponse quoteResponse = + serializers.deserializeWith(InvoiceItemResponse.serializer, response); return quoteResponse.data; } - Future> loadList( + Future> loadList( CompanyEntity company, AuthState auth, int updatedAt) async { String url = auth.url + '/quotes'; @@ -35,16 +35,16 @@ class QuoteRepository { final dynamic response = await webClient.get(url, company.token); - final QuoteListResponse quoteResponse = - serializers.deserializeWith(QuoteListResponse.serializer, response); + final InvoiceListResponse quoteResponse = + serializers.deserializeWith(InvoiceListResponse.serializer, response); return quoteResponse.data; } - Future saveData( - CompanyEntity company, AuthState auth, QuoteEntity quote, + Future saveData( + CompanyEntity company, AuthState auth, InvoiceEntity quote, [EntityAction action]) async { - final data = serializers.serializeWith(QuoteEntity.serializer, quote); + final data = serializers.serializeWith(InvoiceEntity.serializer, quote); dynamic response; if (quote.isNew) { @@ -60,8 +60,8 @@ class QuoteRepository { response = await webClient.put(url, company.token, json.encode(data)); } - final QuoteItemResponse quoteResponse = - serializers.deserializeWith(QuoteItemResponse.serializer, response); + final InvoiceItemResponse quoteResponse = + serializers.deserializeWith(InvoiceItemResponse.serializer, response); return quoteResponse.data; } diff --git a/lib/main.dart b/lib/main.dart index 7f8d72e98..7f4353867 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,6 +34,7 @@ import 'package:invoiceninja_flutter/redux/product/product_middleware.dart'; import 'package:invoiceninja_flutter/redux/invoice/invoice_middleware.dart'; import 'package:invoiceninja_flutter/ui/invoice/invoice_screen.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; + // STARTER: import - do not remove comment import 'package:invoiceninja_flutter/ui/quote/quote_screen.dart'; import 'package:invoiceninja_flutter/ui/quote/edit/quote_edit_vm.dart'; @@ -41,7 +42,6 @@ import 'package:invoiceninja_flutter/ui/quote/view/quote_view_vm.dart'; import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart'; import 'package:invoiceninja_flutter/redux/quote/quote_middleware.dart'; - void main() async { final prefs = await SharedPreferences.getInstance(); final enableDarkMode = prefs.getBool(kSharedPrefEnableDarkMode); @@ -56,8 +56,7 @@ void main() async { ..addAll(createStoreInvoicesMiddleware()) ..addAll(createStorePersistenceMiddleware()) // STARTER: middleware - do not remove comment -..addAll(createStoreQuotesMiddleware()) - + ..addAll(createStoreQuotesMiddleware()) ..addAll([ LoggingMiddleware.printer(), ])); @@ -141,13 +140,12 @@ class InvoiceNinjaAppState extends State { InvoiceEditScreen.route: (context) => InvoiceEditScreen(), InvoiceEmailScreen.route: (context) => InvoiceEmailScreen(), // STARTER: routes - do not remove comment -QuoteScreen.route: (context) { -widget.store.dispatch(LoadQuotes()); -return QuoteScreen(); -}, -QuoteViewScreen.route: (context) => QuoteViewScreen(), -QuoteEditScreen.route: (context) => QuoteEditScreen(), - + QuoteScreen.route: (context) { + widget.store.dispatch(LoadQuotes()); + return QuoteScreen(); + }, + QuoteViewScreen.route: (context) => QuoteViewScreen(), + QuoteEditScreen.route: (context) => QuoteEditScreen(), SettingsScreen.route: (context) => SettingsScreen(), }, ); diff --git a/lib/redux/invoice/invoice_middleware.dart b/lib/redux/invoice/invoice_middleware.dart index 0f6a0ddc8..42366f32c 100644 --- a/lib/redux/invoice/invoice_middleware.dart +++ b/lib/redux/invoice/invoice_middleware.dart @@ -212,9 +212,9 @@ Middleware _saveInvoice(InvoiceRepository repository) { return (Store store, dynamic action, NextDispatcher next) { repository .saveData( - store.state.selectedCompany, store.state.authState, action.invoice) + store.state.selectedCompany, store.state.authState, action.quote) .then((dynamic invoice) { - if (action.invoice.isNew) { + if (action.quote.isNew) { store.dispatch(AddInvoiceSuccess(invoice)); } else { store.dispatch(SaveInvoiceSuccess(invoice)); diff --git a/lib/redux/invoice/invoice_reducer.dart b/lib/redux/invoice/invoice_reducer.dart index d035f277d..bd0e28e44 100644 --- a/lib/redux/invoice/invoice_reducer.dart +++ b/lib/redux/invoice/invoice_reducer.dart @@ -21,7 +21,7 @@ final editingItemReducer = combineReducers([ InvoiceItemEntity editInvoiceItem( InvoiceItemEntity invoiceItem, dynamic action) { - return action.invoiceItem ?? InvoiceItemEntity(); + return action.quoteItem ?? InvoiceItemEntity(); } Reducer dropdownFilterReducer = combineReducers([ @@ -37,9 +37,9 @@ Reducer selectedIdReducer = combineReducers([ TypedReducer( (int selectedId, dynamic action) => action.invoiceId), TypedReducer( - (int selectedId, dynamic action) => action.invoice.id), + (int selectedId, dynamic action) => action.quote.id), TypedReducer( - (int selectedId, dynamic action) => action.invoice.id), + (int selectedId, dynamic action) => action.quote.id), ]); final editingReducer = combineReducers([ @@ -62,7 +62,7 @@ InvoiceEntity _clearEditing(InvoiceEntity client, dynamic action) { } InvoiceEntity _updateEditing(InvoiceEntity invoice, dynamic action) { - return action.invoice; + return action.quote; } InvoiceEntity _addInvoiceItem(InvoiceEntity invoice, AddInvoiceItem action) { @@ -244,7 +244,7 @@ InvoiceState _addInvoice(InvoiceState invoiceState, AddInvoiceSuccess action) { InvoiceState _updateInvoice(InvoiceState invoiceState, dynamic action) { return invoiceState - .rebuild((b) => b..map[action.invoice.id] = action.invoice); + .rebuild((b) => b..map[action.quote.id] = action.quote); } InvoiceState _setNoInvoices( diff --git a/lib/redux/quote/quote_actions.dart b/lib/redux/quote/quote_actions.dart index 9b40267ee..e98f858b8 100644 --- a/lib/redux/quote/quote_actions.dart +++ b/lib/redux/quote/quote_actions.dart @@ -19,7 +19,7 @@ class ViewQuote implements PersistUI { } class EditQuote implements PersistUI { - final QuoteEntity quote; + final InvoiceEntity quote; final InvoiceItemEntity quoteItem; final BuildContext context; final Completer completer; @@ -28,7 +28,7 @@ class EditQuote implements PersistUI { } class ShowEmailQuote { - final QuoteEntity quote; + final InvoiceEntity quote; final BuildContext context; final Completer completer; @@ -42,7 +42,7 @@ class EditQuoteItem implements PersistUI { } class UpdateQuote implements PersistUI { - final QuoteEntity quote; + final InvoiceEntity quote; UpdateQuote(this.quote); } @@ -75,7 +75,7 @@ class LoadQuoteFailure implements StopLoading { } class LoadQuoteSuccess implements StopLoading, PersistData { - final QuoteEntity quote; + final InvoiceEntity quote; LoadQuoteSuccess(this.quote); @@ -99,7 +99,7 @@ class LoadQuotesFailure implements StopLoading { } class LoadQuotesSuccess implements StopLoading, PersistData { - final BuiltList quotes; + final BuiltList quotes; LoadQuotesSuccess(this.quotes); @@ -136,19 +136,19 @@ class DeleteQuoteItem implements PersistUI { class SaveQuoteRequest implements StartSaving { final Completer completer; - final QuoteEntity quote; + final InvoiceEntity quote; SaveQuoteRequest({this.completer, this.quote}); } class SaveQuoteSuccess implements StopSaving, PersistData, PersistUI { - final QuoteEntity quote; + final InvoiceEntity quote; SaveQuoteSuccess(this.quote); } class AddQuoteSuccess implements StopSaving, PersistData, PersistUI { - final QuoteEntity quote; + final InvoiceEntity quote; AddQuoteSuccess(this.quote); } @@ -186,13 +186,13 @@ class MarkSentQuoteRequest implements StartSaving { } class MarkSentQuoteSuccess implements StopSaving, PersistData { - final QuoteEntity quote; + final InvoiceEntity quote; MarkSentQuoteSuccess(this.quote); } class MarkSentQuoteFailure implements StopSaving { - final QuoteEntity quote; + final InvoiceEntity quote; MarkSentQuoteFailure(this.quote); } @@ -205,13 +205,13 @@ class ArchiveQuoteRequest implements StartSaving { } class ArchiveQuoteSuccess implements StopSaving, PersistData { - final QuoteEntity quote; + final InvoiceEntity quote; ArchiveQuoteSuccess(this.quote); } class ArchiveQuoteFailure implements StopSaving { - final QuoteEntity quote; + final InvoiceEntity quote; ArchiveQuoteFailure(this.quote); } @@ -224,13 +224,13 @@ class DeleteQuoteRequest implements StartSaving { } class DeleteQuoteSuccess implements StopSaving, PersistData { - final QuoteEntity quote; + final InvoiceEntity quote; DeleteQuoteSuccess(this.quote); } class DeleteQuoteFailure implements StopSaving { - final QuoteEntity quote; + final InvoiceEntity quote; DeleteQuoteFailure(this.quote); } @@ -243,13 +243,13 @@ class RestoreQuoteRequest implements StartSaving { } class RestoreQuoteSuccess implements StopSaving, PersistData { - final QuoteEntity quote; + final InvoiceEntity quote; RestoreQuoteSuccess(this.quote); } class RestoreQuoteFailure implements StopSaving { - final QuoteEntity quote; + final InvoiceEntity quote; RestoreQuoteFailure(this.quote); } diff --git a/lib/redux/quote/quote_reducer.dart b/lib/redux/quote/quote_reducer.dart index bcee79f05..6709b287d 100644 --- a/lib/redux/quote/quote_reducer.dart +++ b/lib/redux/quote/quote_reducer.dart @@ -20,22 +20,22 @@ Reducer selectedIdReducer = combineReducers([ (int selectedId, dynamic action) => action.quote.id), ]); -final editingReducer = combineReducers([ - TypedReducer(_updateEditing), - TypedReducer(_updateEditing), - TypedReducer(_updateEditing), - TypedReducer(_updateEditing), - TypedReducer(_updateEditing), - TypedReducer(_updateEditing), - TypedReducer(_updateEditing), - TypedReducer(_clearEditing), +final editingReducer = combineReducers([ + TypedReducer(_updateEditing), + TypedReducer(_updateEditing), + TypedReducer(_updateEditing), + TypedReducer(_updateEditing), + TypedReducer(_updateEditing), + TypedReducer(_updateEditing), + TypedReducer(_updateEditing), + TypedReducer(_clearEditing), ]); -QuoteEntity _clearEditing(QuoteEntity quote, dynamic action) { - return QuoteEntity(); +InvoiceEntity _clearEditing(InvoiceEntity quote, dynamic action) { + return InvoiceEntity(); } -QuoteEntity _updateEditing(QuoteEntity quote, dynamic action) { +InvoiceEntity _updateEditing(InvoiceEntity quote, dynamic action) { return action.quote; } diff --git a/lib/redux/quote/quote_selectors.dart b/lib/redux/quote/quote_selectors.dart index 8fafb6b75..d9e1348cb 100644 --- a/lib/redux/quote/quote_selectors.dart +++ b/lib/redux/quote/quote_selectors.dart @@ -3,52 +3,96 @@ 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 memoizedDropdownQuoteList = memo2( - (BuiltMap quoteMap, BuiltList quoteList) => - dropdownQuotesSelector(quoteMap, quoteList)); - -List dropdownQuotesSelector( - BuiltMap quoteMap, BuiltList quoteList) { - final list = - quoteList.where((quoteId) => quoteMap[quoteId].isActive).toList(); - - list.sort((quoteAId, quoteBId) { - final quoteA = quoteMap[quoteAId]; - final quoteB = quoteMap[quoteBId]; - return quoteA.compareTo(quoteB, QuoteFields.quoteNumber, true); - }); - - return list; +ClientEntity quoteClientSelector( + InvoiceEntity invoice, BuiltMap clientMap) { + return clientMap[invoice.clientId]; } -var memoizedFilteredQuoteList = memo3((BuiltMap quoteMap, - BuiltList quoteList, ListUIState quoteListState) => - filteredQuotesSelector(quoteMap, quoteList, quoteListState)); +var memoizedFilteredQuoteList = memo4( + (BuiltMap invoiceMap, + BuiltList invoiceList, + BuiltMap clientMap, + ListUIState invoiceListState) => + filteredQuotesSelector( + invoiceMap, invoiceList, clientMap, invoiceListState)); -List filteredQuotesSelector(BuiltMap quoteMap, - BuiltList quoteList, ListUIState quoteListState) { - final list = quoteList.where((quoteId) { - final quote = quoteMap[quoteId]; - if (!quote.matchesStates(quoteListState.stateFilters)) { +List filteredQuotesSelector( + BuiltMap invoiceMap, + BuiltList invoiceList, + BuiltMap clientMap, + ListUIState invoiceListState) { + final list = invoiceList.where((invoiceId) { + final invoice = invoiceMap[invoiceId]; + final client = clientMap[invoice.clientId]; + if (client == null || ! client.isActive) { return false; } - if (quoteListState.custom1Filters.isNotEmpty && - !quoteListState.custom1Filters.contains(quote.customTextValue1)) { + if (!invoice.matchesStates(invoiceListState.stateFilters)) { return false; } - if (quoteListState.custom2Filters.isNotEmpty && - !quoteListState.custom2Filters.contains(quote.customTextValue2)) { + if (!invoice.matchesStatuses(invoiceListState.statusFilters)) { return false; } - return quote.matchesFilter(quoteListState.filter); + if (!invoice.matchesFilter(invoiceListState.filter) && + !client.matchesFilter(invoiceListState.filter)) { + return false; + } + if (invoiceListState.filterClientId != null && + invoice.clientId != invoiceListState.filterClientId) { + return false; + } + if (invoiceListState.custom1Filters.isNotEmpty && + !invoiceListState.custom1Filters.contains(invoice.customTextValue1)) { + return false; + } + if (invoiceListState.custom2Filters.isNotEmpty && + !invoiceListState.custom2Filters.contains(invoice.customTextValue2)) { + return false; + } + return true; }).toList(); - list.sort((quoteAId, quoteBId) { - final quoteA = quoteMap[quoteAId]; - final quoteB = quoteMap[quoteBId]; - return quoteA.compareTo( - quoteB, quoteListState.sortField, quoteListState.sortAscending); + list.sort((invoiceAId, invoiceBId) { + return invoiceMap[invoiceAId].compareTo(invoiceMap[invoiceBId], + invoiceListState.sortField, invoiceListState.sortAscending); }); return list; } + +var memoizedQuoteStatsForClient = memo4((int clientId, + BuiltMap invoiceMap, + String activeLabel, + String archivedLabel) => + quoteStatsForClient(clientId, invoiceMap, activeLabel, archivedLabel)); + +String quoteStatsForClient( + int clientId, + BuiltMap invoiceMap, + String activeLabel, + String archivedLabel) { + int countActive = 0; + int countArchived = 0; + invoiceMap.forEach((invoiceId, invoice) { + if (invoice.clientId == clientId) { + if (invoice.isActive) { + countActive++; + } else if (invoice.isArchived) { + countArchived++; + } + } + }); + + String str = ''; + if (countActive > 0) { + str = '$countActive $activeLabel'; + if (countArchived > 0) { + str += ' • '; + } + } + if (countArchived > 0) { + str += '$countArchived $archivedLabel'; + } + + return str; +} diff --git a/lib/redux/quote/quote_state.dart b/lib/redux/quote/quote_state.dart index 0fec8df4a..92ed18bdd 100644 --- a/lib/redux/quote/quote_state.dart +++ b/lib/redux/quote/quote_state.dart @@ -2,7 +2,7 @@ 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/quote_model.dart'; +import 'package:invoiceninja_flutter/data/models/invoice_model.dart'; import 'package:invoiceninja_flutter/redux/ui/entity_ui_state.dart'; import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart'; @@ -13,7 +13,7 @@ abstract class QuoteState implements Built { factory QuoteState() { return _$QuoteState._( lastUpdated: 0, - map: BuiltMap(), + map: BuiltMap(), list: BuiltList(), ); } @@ -22,7 +22,7 @@ abstract class QuoteState implements Built { @nullable int get lastUpdated; - BuiltMap get map; + BuiltMap get map; BuiltList get list; bool get isStale { @@ -42,15 +42,19 @@ abstract class QuoteUIState extends Object with EntityUIState implements Built editing.isNew; diff --git a/lib/redux/quote/quote_state.g.dart b/lib/redux/quote/quote_state.g.dart index 2a90428ae..bdce53938 100644 --- a/lib/redux/quote/quote_state.g.dart +++ b/lib/redux/quote/quote_state.g.dart @@ -33,7 +33,7 @@ class _$QuoteStateSerializer implements StructuredSerializer { 'map', serializers.serialize(object.map, specifiedType: const FullType(BuiltMap, - const [const FullType(int), const FullType(QuoteEntity)])), + const [const FullType(int), const FullType(InvoiceEntity)])), 'list', serializers.serialize(object.list, specifiedType: @@ -68,7 +68,7 @@ class _$QuoteStateSerializer implements StructuredSerializer { result.map.replace(serializers.deserialize(value, specifiedType: const FullType(BuiltMap, const [ const FullType(int), - const FullType(QuoteEntity) + const FullType(InvoiceEntity) ])) as BuiltMap); break; case 'list': @@ -105,7 +105,13 @@ class _$QuoteUIStateSerializer implements StructuredSerializer { result ..add('editing') ..add(serializers.serialize(object.editing, - specifiedType: const FullType(QuoteEntity))); + specifiedType: const FullType(InvoiceEntity))); + } + if (object.editingItem != null) { + result + ..add('editingItem') + ..add(serializers.serialize(object.editingItem, + specifiedType: const FullType(InvoiceItemEntity))); } return result; @@ -124,7 +130,12 @@ class _$QuoteUIStateSerializer implements StructuredSerializer { switch (key) { case 'editing': result.editing.replace(serializers.deserialize(value, - specifiedType: const FullType(QuoteEntity)) as QuoteEntity); + specifiedType: const FullType(InvoiceEntity)) as InvoiceEntity); + break; + case 'editingItem': + result.editingItem.replace(serializers.deserialize(value, + specifiedType: const FullType(InvoiceItemEntity)) + as InvoiceItemEntity); break; case 'selectedId': result.selectedId = serializers.deserialize(value, @@ -145,7 +156,7 @@ class _$QuoteState extends QuoteState { @override final int lastUpdated; @override - final BuiltMap map; + final BuiltMap map; @override final BuiltList list; @@ -196,10 +207,10 @@ class QuoteStateBuilder implements Builder { int get lastUpdated => _$this._lastUpdated; set lastUpdated(int lastUpdated) => _$this._lastUpdated = lastUpdated; - MapBuilder _map; - MapBuilder get map => - _$this._map ??= new MapBuilder(); - set map(MapBuilder map) => _$this._map = map; + MapBuilder _map; + MapBuilder get map => + _$this._map ??= new MapBuilder(); + set map(MapBuilder map) => _$this._map = map; ListBuilder _list; ListBuilder get list => _$this._list ??= new ListBuilder(); @@ -255,7 +266,9 @@ class QuoteStateBuilder implements Builder { class _$QuoteUIState extends QuoteUIState { @override - final QuoteEntity editing; + final InvoiceEntity editing; + @override + final InvoiceItemEntity editingItem; @override final int selectedId; @override @@ -264,7 +277,8 @@ class _$QuoteUIState extends QuoteUIState { factory _$QuoteUIState([void updates(QuoteUIStateBuilder b)]) => (new QuoteUIStateBuilder()..update(updates)).build(); - _$QuoteUIState._({this.editing, this.selectedId, this.listUIState}) + _$QuoteUIState._( + {this.editing, this.editingItem, this.selectedId, this.listUIState}) : super._() { if (selectedId == null) throw new BuiltValueNullFieldError('QuoteUIState', 'selectedId'); @@ -284,13 +298,16 @@ class _$QuoteUIState extends QuoteUIState { if (identical(other, this)) return true; if (other is! QuoteUIState) return false; return editing == other.editing && + editingItem == other.editingItem && selectedId == other.selectedId && listUIState == other.listUIState; } @override int get hashCode { - return $jf($jc($jc($jc(0, editing.hashCode), selectedId.hashCode), + return $jf($jc( + $jc($jc($jc(0, editing.hashCode), editingItem.hashCode), + selectedId.hashCode), listUIState.hashCode)); } @@ -298,6 +315,7 @@ class _$QuoteUIState extends QuoteUIState { String toString() { return (newBuiltValueToStringHelper('QuoteUIState') ..add('editing', editing) + ..add('editingItem', editingItem) ..add('selectedId', selectedId) ..add('listUIState', listUIState)) .toString(); @@ -308,10 +326,16 @@ class QuoteUIStateBuilder implements Builder { _$QuoteUIState _$v; - QuoteEntityBuilder _editing; - QuoteEntityBuilder get editing => - _$this._editing ??= new QuoteEntityBuilder(); - set editing(QuoteEntityBuilder editing) => _$this._editing = editing; + InvoiceEntityBuilder _editing; + InvoiceEntityBuilder get editing => + _$this._editing ??= new InvoiceEntityBuilder(); + set editing(InvoiceEntityBuilder editing) => _$this._editing = editing; + + InvoiceItemEntityBuilder _editingItem; + InvoiceItemEntityBuilder get editingItem => + _$this._editingItem ??= new InvoiceItemEntityBuilder(); + set editingItem(InvoiceItemEntityBuilder editingItem) => + _$this._editingItem = editingItem; int _selectedId; int get selectedId => _$this._selectedId; @@ -328,6 +352,7 @@ class QuoteUIStateBuilder QuoteUIStateBuilder get _$this { if (_$v != null) { _editing = _$v.editing?.toBuilder(); + _editingItem = _$v.editingItem?.toBuilder(); _selectedId = _$v.selectedId; _listUIState = _$v.listUIState?.toBuilder(); _$v = null; @@ -353,6 +378,7 @@ class QuoteUIStateBuilder _$result = _$v ?? new _$QuoteUIState._( editing: _editing?.build(), + editingItem: _editingItem?.build(), selectedId: selectedId, listUIState: listUIState.build()); } catch (_) { @@ -360,6 +386,8 @@ class QuoteUIStateBuilder try { _$failedField = 'editing'; _editing?.build(); + _$failedField = 'editingItem'; + _editingItem?.build(); _$failedField = 'listUIState'; listUIState.build(); diff --git a/lib/redux/ui/ui_state.dart b/lib/redux/ui/ui_state.dart index 76a88366a..026617e87 100644 --- a/lib/redux/ui/ui_state.dart +++ b/lib/redux/ui/ui_state.dart @@ -4,14 +4,13 @@ import 'package:invoiceninja_flutter/redux/client/client_state.dart'; import 'package:invoiceninja_flutter/redux/invoice/invoice_state.dart'; import 'package:invoiceninja_flutter/redux/product/product_state.dart'; import 'package:invoiceninja_flutter/ui/auth/login_vm.dart'; + // STARTER: import - do not remove comment import 'package:invoiceninja_flutter/redux/quote/quote_state.dart'; - part 'ui_state.g.dart'; abstract class UIState implements Built { - factory UIState({bool enableDarkMode}) { return _$UIState._( selectedCompanyIndex: 0, @@ -21,26 +20,29 @@ abstract class UIState implements Built { clientUIState: ClientUIState(), invoiceUIState: InvoiceUIState(), // STARTER: constructor - do not remove comment -quoteUIState: QuoteUIState(), - + quoteUIState: QuoteUIState(), ); } + UIState._(); int get selectedCompanyIndex; + String get currentRoute; + bool get enableDarkMode; + ProductUIState get productUIState; + ClientUIState get clientUIState; + InvoiceUIState get invoiceUIState; @nullable String get filter; // STARTER: properties - do not remove comment -QuoteUIState get quoteUIState; - + QuoteUIState get quoteUIState; static Serializer get serializer => _$uIStateSerializer; } - diff --git a/lib/ui/invoice/edit/invoice_edit.dart b/lib/ui/invoice/edit/invoice_edit.dart index 9f79fe14d..d479e5ed3 100644 --- a/lib/ui/invoice/edit/invoice_edit.dart +++ b/lib/ui/invoice/edit/invoice_edit.dart @@ -1,141 +1,141 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_details_vm.dart'; -import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_items_vm.dart'; -import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_vm.dart'; -import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_item_selector.dart'; -import 'package:invoiceninja_flutter/utils/formatting.dart'; -import 'package:invoiceninja_flutter/utils/localization.dart'; -import 'package:invoiceninja_flutter/ui/app/buttons/refresh_icon_button.dart'; + import 'package:flutter/foundation.dart'; + import 'package:flutter/material.dart'; + import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_details_vm.dart'; + import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_items_vm.dart'; + import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_vm.dart'; + import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_item_selector.dart'; + import 'package:invoiceninja_flutter/utils/formatting.dart'; + import 'package:invoiceninja_flutter/utils/localization.dart'; + import 'package:invoiceninja_flutter/ui/app/buttons/refresh_icon_button.dart'; -class InvoiceEdit extends StatefulWidget { - final InvoiceEditVM viewModel; + class InvoiceEdit extends StatefulWidget { + final InvoiceEditVM viewModel; - const InvoiceEdit({ - Key key, - @required this.viewModel, - }) : super(key: key); + const InvoiceEdit({ + Key key, + @required this.viewModel, + }) : super(key: key); - @override - _InvoiceEditState createState() => _InvoiceEditState(); -} - -class _InvoiceEditState extends State - with SingleTickerProviderStateMixin { - TabController _controller; - static final GlobalKey _formKey = GlobalKey(); - - static const kDetailsScreen = 0; - static const kItemScreen = 1; - - @override - void initState() { - super.initState(); - - final invoice = widget.viewModel.invoice; - final invoiceItem = widget.viewModel.invoiceItem; - - final index = - invoice.invoiceItems.contains(invoiceItem) ? kItemScreen : kDetailsScreen; - _controller = - TabController(vsync: this, length: 2, initialIndex: index); + @override + _InvoiceEditState createState() => _InvoiceEditState(); } - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } + class _InvoiceEditState extends State + with SingleTickerProviderStateMixin { + TabController _controller; + static final GlobalKey _formKey = GlobalKey(); - @override - Widget build(BuildContext context) { - final localization = AppLocalization.of(context); - final viewModel = widget.viewModel; - final invoice = viewModel.invoice; + static const kDetailsScreen = 0; + static const kItemScreen = 1; - return WillPopScope( - onWillPop: () async { - viewModel.onBackPressed(); - return true; - }, - child: Scaffold( - appBar: AppBar( - title: Text(invoice.isNew - ? localization.newInvoice - : '${localization.invoice} ${viewModel.origInvoice.invoiceNumber}'), - actions: [ - RefreshIconButton( - icon: Icons.cloud_upload, - tooltip: localization.save, - isVisible: !invoice.isDeleted, - isSaving: widget.viewModel.isSaving, - isDirty: invoice.isNew || invoice != viewModel.origInvoice, - onPressed: () { - if (!_formKey.currentState.validate()) { - return; - } + @override + void initState() { + super.initState(); - widget.viewModel.onSavePressed(context); - }, - ) - ], - bottom: TabBar( - controller: _controller, - //isScrollable: true, - tabs: [ - Tab( - text: localization.details, - ), - Tab( - text: localization.items, - ), + final invoice = widget.viewModel.invoice; + final invoiceItem = widget.viewModel.invoiceItem; + + final index = + invoice.invoiceItems.contains(invoiceItem) ? kItemScreen : kDetailsScreen; + _controller = + TabController(vsync: this, length: 2, initialIndex: index); + } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final localization = AppLocalization.of(context); + final viewModel = widget.viewModel; + final invoice = viewModel.invoice; + + return WillPopScope( + onWillPop: () async { + viewModel.onBackPressed(); + return true; + }, + child: Scaffold( + appBar: AppBar( + title: Text(invoice.isNew + ? localization.newInvoice + : '${localization.invoice} ${viewModel.origInvoice.invoiceNumber}'), + actions: [ + RefreshIconButton( + icon: Icons.cloud_upload, + tooltip: localization.save, + isVisible: !invoice.isDeleted, + isSaving: widget.viewModel.isSaving, + isDirty: invoice.isNew || invoice != viewModel.origInvoice, + onPressed: () { + if (!_formKey.currentState.validate()) { + return; + } + + widget.viewModel.onSavePressed(context); + }, + ) ], + bottom: TabBar( + controller: _controller, + //isScrollable: true, + tabs: [ + Tab( + text: localization.details, + ), + Tab( + text: localization.items, + ), + ], + ), ), - ), - body: Form( - key: _formKey, - child: TabBarView( - controller: _controller, - children: [ - InvoiceEditDetailsScreen(), - InvoiceEditItemsScreen(), - ], + body: Form( + key: _formKey, + child: TabBarView( + controller: _controller, + children: [ + InvoiceEditDetailsScreen(), + InvoiceEditItemsScreen(), + ], + ), ), - ), - bottomNavigationBar: BottomAppBar( - color: Theme.of(context).primaryColor, - shape: CircularNotchedRectangle(), - child: Padding( - padding: const EdgeInsets.all(14.0), - child: Text( - '${localization.total}: ${formatNumber(invoice.calculateTotal(viewModel.company.enableInclusiveTaxes), context, clientId: viewModel.invoice.clientId)}', - style: TextStyle( - //color: Theme.of(context).selectedRowColor, - color: Colors.white, - fontSize: 18.0, + bottomNavigationBar: BottomAppBar( + color: Theme.of(context).primaryColor, + shape: CircularNotchedRectangle(), + child: Padding( + padding: const EdgeInsets.all(14.0), + child: Text( + '${localization.total}: ${formatNumber(invoice.calculateTotal(viewModel.company.enableInclusiveTaxes), context, clientId: viewModel.invoice.clientId)}', + style: TextStyle( + //color: Theme.of(context).selectedRowColor, + color: Colors.white, + fontSize: 18.0, + ), ), ), ), + floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, + floatingActionButton: FloatingActionButton( + backgroundColor: Theme.of(context).primaryColorDark, + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return InvoiceItemSelector( + onItemsSelected: (items) { + viewModel.onItemsAdded(items); + _controller.animateTo(kItemScreen); + }, + ); + }); + }, + child: const Icon(Icons.add, color: Colors.white), + tooltip: localization.addItem, + ), ), - floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, - floatingActionButton: FloatingActionButton( - backgroundColor: Theme.of(context).primaryColorDark, - onPressed: () { - showDialog( - context: context, - builder: (BuildContext context) { - return InvoiceItemSelector( - onItemsSelected: (items) { - viewModel.onItemsAdded(items); - _controller.animateTo(kItemScreen); - }, - ); - }); - }, - child: const Icon(Icons.add, color: Colors.white), - tooltip: localization.addItem, - ), - ), - ); + ); + } } -} diff --git a/lib/ui/invoice/invoice_list_item.dart b/lib/ui/invoice/invoice_list_item.dart index b07fd0f13..5b3f8b729 100644 --- a/lib/ui/invoice/invoice_list_item.dart +++ b/lib/ui/invoice/invoice_list_item.dart @@ -30,7 +30,8 @@ class InvoiceListItem extends StatelessWidget { Widget build(BuildContext context) { final localization = AppLocalization.of(context); final filterMatch = filter != null && filter.isNotEmpty - ? (invoice.matchesFilterValue(filter) ?? client.matchesFilterValue(filter)) + ? (invoice.matchesFilterValue(filter) ?? + client.matchesFilterValue(filter)) : null; return DismissibleEntity( @@ -71,10 +72,15 @@ class InvoiceListItem extends StatelessWidget { overflow: TextOverflow.ellipsis, ), ), - Text(invoice.isPastDue ? localization.pastDue : localization.lookup('invoice_status_${invoice.invoiceStatusId}'), + Text( + invoice.isPastDue + ? localization.pastDue + : localization.lookup( + 'invoice_status_${invoice.invoiceStatusId}'), style: TextStyle( - color: - invoice.isPastDue ? Colors.red : InvoiceStatusColors.colors[invoice.invoiceStatusId], + color: invoice.isPastDue + ? Colors.red + : InvoiceStatusColors.colors[invoice.invoiceStatusId], )), ], ), @@ -85,4 +91,3 @@ class InvoiceListItem extends StatelessWidget { ); } } - diff --git a/lib/ui/invoice/view/invoice_view_vm.dart b/lib/ui/invoice/view/invoice_view_vm.dart index a05429518..a714b4bb9 100644 --- a/lib/ui/invoice/view/invoice_view_vm.dart +++ b/lib/ui/invoice/view/invoice_view_vm.dart @@ -146,7 +146,7 @@ class InvoiceViewVM { bool operator ==(dynamic other) => client == other.client && company == other.company && - invoice == other.invoice && + invoice == other.quote && isSaving == other.isSaving && isDirty == other.isDirty; diff --git a/lib/ui/quote/edit/quote_edit.dart b/lib/ui/quote/edit/quote_edit.dart index 216923b31..077f93181 100644 --- a/lib/ui/quote/edit/quote_edit.dart +++ b/lib/ui/quote/edit/quote_edit.dart @@ -1,13 +1,17 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_redux_starter/ui/app/form_card.dart'; -import 'package:flutter_redux_starter/ui/quote/edit/quote_edit_vm.dart'; -import 'package:flutter_redux_starter/ui/app/save_icon_button.dart'; +import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_details_vm.dart'; +import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_items_vm.dart'; +import 'package:invoiceninja_flutter/ui/quote/edit/quote_edit_vm.dart'; +import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_item_selector.dart'; +import 'package:invoiceninja_flutter/utils/formatting.dart'; +import 'package:invoiceninja_flutter/utils/localization.dart'; +import 'package:invoiceninja_flutter/ui/app/buttons/refresh_icon_button.dart'; class QuoteEdit extends StatefulWidget { final QuoteEditVM viewModel; - QuoteEdit({ + const QuoteEdit({ Key key, @required this.viewModel, }) : super(key: key); @@ -16,52 +20,38 @@ class QuoteEdit extends StatefulWidget { _QuoteEditState createState() => _QuoteEditState(); } -class _QuoteEditState extends State { +class _QuoteEditState extends State + with SingleTickerProviderStateMixin { + TabController _controller; static final GlobalKey _formKey = GlobalKey(); - // STARTER: controllers - do not remove comment - - var _controllers = []; + static const kDetailsScreen = 0; + static const kItemScreen = 1; @override - void didChangeDependencies() { + void initState() { + super.initState(); - _controllers = [ - // STARTER: array - do not remove comment - ]; + final invoice = widget.viewModel.quote; + final invoiceItem = widget.viewModel.quoteItem; - _controllers.forEach((controller) => controller.removeListener(_onChanged)); - - var quote = widget.viewModel.quote; - // STARTER: read value - do not remove comment - - _controllers.forEach((controller) => controller.addListener(_onChanged)); - - super.didChangeDependencies(); + final index = invoice.invoiceItems.contains(invoiceItem) + ? kItemScreen + : kDetailsScreen; + _controller = TabController(vsync: this, length: 2, initialIndex: index); } @override void dispose() { - _controllers.forEach((controller) { - controller.removeListener(_onChanged); - controller.dispose(); - }); - + _controller.dispose(); super.dispose(); } - _onChanged() { - var quote = widget.viewModel.quote.rebuild((b) => b - // STARTER: set value - do not remove comment - ); - if (quote != widget.viewModel.quote) { - widget.viewModel.onChanged(quote); - } - } - @override Widget build(BuildContext context) { - var viewModel = widget.viewModel; + final localization = AppLocalization.of(context); + final viewModel = widget.viewModel; + final invoice = viewModel.quote; return WillPopScope( onWillPop: () async { @@ -70,36 +60,81 @@ class _QuoteEditState extends State { }, child: Scaffold( appBar: AppBar( - title: Text(viewModel.quote.isNew - ? 'New Quote' - : viewModel.quote.displayName), + title: Text(invoice.isNew + ? localization.newQuote + : '${localization.quote} ${viewModel.origQuote.invoiceNumber}'), actions: [ - Builder(builder: (BuildContext context) { - return SaveIconButton( - isLoading: viewModel.isLoading, - onPressed: () { - if (!_formKey.currentState.validate()) { - return; - } + RefreshIconButton( + icon: Icons.cloud_upload, + tooltip: localization.save, + isVisible: !invoice.isDeleted, + isSaving: widget.viewModel.isSaving, + isDirty: invoice.isNew || invoice != viewModel.origQuote, + onPressed: () { + if (!_formKey.currentState.validate()) { + return; + } - viewModel.onSavePressed(context); - }, - ); - }), + widget.viewModel.onSavePressed(context); + }, + ) ], - ), - body: Form( - key: _formKey, - child: ListView( - children: [ - FormCard( - children: [ - // STARTER: widgets - do not remove comment - ], + bottom: TabBar( + controller: _controller, + //isScrollable: true, + tabs: [ + Tab( + text: localization.details, + ), + Tab( + text: localization.items, ), ], ), ), + body: Form( + key: _formKey, + child: TabBarView( + controller: _controller, + children: [ + InvoiceEditDetailsScreen(), + InvoiceEditItemsScreen(), + ], + ), + ), + bottomNavigationBar: BottomAppBar( + color: Theme.of(context).primaryColor, + shape: CircularNotchedRectangle(), + child: Padding( + padding: const EdgeInsets.all(14.0), + child: Text( + '${localization.total}: ${formatNumber(invoice.calculateTotal(viewModel.company.enableInclusiveTaxes), context, clientId: viewModel.quote.clientId)}', + style: TextStyle( + //color: Theme.of(context).selectedRowColor, + color: Colors.white, + fontSize: 18.0, + ), + ), + ), + ), + floatingActionButtonLocation: FloatingActionButtonLocation.endDocked, + floatingActionButton: FloatingActionButton( + backgroundColor: Theme.of(context).primaryColorDark, + onPressed: () { + showDialog( + context: context, + builder: (BuildContext context) { + return InvoiceItemSelector( + onItemsSelected: (items) { + viewModel.onItemsAdded(items); + _controller.animateTo(kItemScreen); + }, + ); + }); + }, + child: const Icon(Icons.add, color: Colors.white), + tooltip: localization.addItem, + ), ), ); } diff --git a/lib/ui/quote/edit/quote_edit_vm.dart b/lib/ui/quote/edit/quote_edit_vm.dart index 83d37b474..9000ccf8f 100644 --- a/lib/ui/quote/edit/quote_edit_vm.dart +++ b/lib/ui/quote/edit/quote_edit_vm.dart @@ -2,18 +2,21 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; -import 'package:flutter_redux_starter/redux/ui/ui_actions.dart'; -import 'package:flutter_redux_starter/ui/quote/quote_screen.dart'; +import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; +import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart'; +import 'package:invoiceninja_flutter/ui/invoice/invoice_screen.dart'; +import 'package:invoiceninja_flutter/ui/invoice/view/invoice_view_vm.dart'; +import 'package:invoiceninja_flutter/ui/quote/edit/quote_edit.dart'; import 'package:redux/redux.dart'; -import 'package:flutter_redux_starter/redux/quote/quote_actions.dart'; -import 'package:flutter_redux_starter/data/models/quote_model.dart'; -import 'package:flutter_redux_starter/ui/quote/edit/quote_edit.dart'; -import 'package:flutter_redux_starter/redux/app/app_state.dart'; -import 'package:flutter_redux_starter/ui/app/icon_message.dart'; +import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart'; +import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; class QuoteEditScreen extends StatelessWidget { - static final String route = '/quote/edit'; - QuoteEditScreen({Key key}) : super(key: key); + static const String route = '/quote/edit'; + + const QuoteEditScreen({Key key}) : super(key: key); @override Widget build(BuildContext context) { @@ -31,45 +34,62 @@ class QuoteEditScreen extends StatelessWidget { } class QuoteEditVM { - final QuoteEntity quote; - final Function(QuoteEntity) onChanged; + final CompanyEntity company; + final InvoiceEntity quote; + final InvoiceItemEntity quoteItem; + final InvoiceEntity origQuote; final Function(BuildContext) onSavePressed; + final Function(List) onItemsAdded; final Function onBackPressed; - final bool isLoading; + final bool isSaving; QuoteEditVM({ + @required this.company, @required this.quote, - @required this.onChanged, + @required this.quoteItem, + @required this.origQuote, @required this.onSavePressed, + @required this.onItemsAdded, @required this.onBackPressed, - @required this.isLoading, + @required this.isSaving, }); factory QuoteEditVM.fromStore(Store store) { - final quote = store.state.quoteUIState.selected; + final AppState state = store.state; + final invoice = state.invoiceUIState.editing; return QuoteEditVM( - isLoading: store.state.isLoading, - quote: quote, - onChanged: (QuoteEntity quote) { - store.dispatch(UpdateQuote(quote)); - }, - onBackPressed: () { - store.dispatch(UpdateCurrentRoute(QuoteScreen.route)); - }, + company: state.selectedCompany, + isSaving: state.isSaving, + quote: invoice, + quoteItem: state.invoiceUIState.editingItem, + origQuote: store.state.invoiceState.map[invoice.id], + onBackPressed: () => + store.dispatch(UpdateCurrentRoute(InvoiceScreen.route)), onSavePressed: (BuildContext context) { - final Completer completer = new Completer(); - store.dispatch(SaveQuoteRequest(completer: completer, quote: quote)); - return completer.future.then((_) { - Scaffold.of(context).showSnackBar(SnackBar( - content: IconMessage( - message: quote.isNew - ? 'Successfully Created Quote' - : 'Successfully Updated Quote', - ), - duration: Duration(seconds: 3))); + final Completer completer = Completer(); + store.dispatch( + SaveInvoiceRequest(completer: completer, invoice: invoice)); + return completer.future.then((savedInvoice) { + if (invoice.isNew) { + Navigator.of(context).pushReplacementNamed(InvoiceViewScreen.route); + } else { + Navigator.of(context).pop(savedInvoice); + } + }).catchError((Object error) { + showDialog( + context: context, + builder: (BuildContext context) { + return ErrorDialog(error); + }); }); }, + onItemsAdded: (items) { + if (items.length == 1) { + store.dispatch(EditInvoiceItem(items[0])); + } + store.dispatch(AddInvoiceItems(items)); + }, ); } } diff --git a/lib/ui/quote/quote_list.dart b/lib/ui/quote/quote_list.dart index 26a1883d6..c06e8f96c 100644 --- a/lib/ui/quote/quote_list.dart +++ b/lib/ui/quote/quote_list.dart @@ -1,9 +1,11 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; +import 'package:invoiceninja_flutter/data/models/invoice_model.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart'; import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart'; -import 'package:invoiceninja_flutter/ui/quote/quote_list_item.dart'; +import 'package:invoiceninja_flutter/ui/invoice/invoice_list_item.dart'; +import 'package:invoiceninja_flutter/ui/invoice/invoice_list_vm.dart'; import 'package:invoiceninja_flutter/ui/quote/quote_list_vm.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; @@ -15,98 +17,167 @@ class QuoteList extends StatelessWidget { @required this.viewModel, }) : super(key: key); - @override - Widget build(BuildContext context) { - if (!viewModel.isLoaded) { - return LoadingIndicator(); - } else if (viewModel.quoteList.isEmpty) { - return Opacity( - opacity: 0.5, - child: Center( - child: Text( - AppLocalization.of(context).noRecordsFound, - style: TextStyle( - fontSize: 18.0, - ), - ), - ), - ); - } - - return _buildListView(context); - } - - void _showMenu(BuildContext context, QuoteEntity quote) async { + void _showMenu( + BuildContext context, InvoiceEntity invoice, ClientEntity client) async { final user = viewModel.user; final message = await showDialog( context: context, builder: (BuildContext context) => SimpleDialog(children: [ - user.canCreate(EntityType.quote) - ? ListTile( - leading: Icon(Icons.control_point_duplicate), - title: Text(AppLocalization.of(context).clone), - onTap: () => viewModel.onEntityAction( - context, quote, EntityAction.clone), - ) - : Container(), - Divider(), - user.canEditEntity(quote) && !quote.isActive - ? ListTile( - leading: Icon(Icons.restore), - title: Text(AppLocalization.of(context).restore), - onTap: () => viewModel.onEntityAction( - context, quote, EntityAction.restore), - ) - : Container(), - user.canEditEntity(quote) && quote.isActive - ? ListTile( - leading: Icon(Icons.archive), - title: Text(AppLocalization.of(context).archive), - onTap: () => viewModel.onEntityAction( - context, quote, EntityAction.archive), - ) - : Container(), - user.canEditEntity(quote) && !quote.isDeleted - ? ListTile( - leading: Icon(Icons.delete), - title: Text(AppLocalization.of(context).delete), - onTap: () => viewModel.onEntityAction( - context, quote, EntityAction.delete), - ) - : Container(), - ])); + user.canCreate(EntityType.invoice) + ? ListTile( + leading: Icon(Icons.control_point_duplicate), + title: Text(AppLocalization.of(context).clone), + onTap: () => viewModel.onEntityAction( + context, invoice, EntityAction.clone), + ) + : Container(), + user.canEditEntity(invoice) && !invoice.isPublic + ? ListTile( + leading: Icon(Icons.publish), + title: Text(AppLocalization.of(context).markSent), + onTap: () => viewModel.onEntityAction( + context, invoice, EntityAction.markSent), + ) + : Container(), + user.canEditEntity(invoice) && client.hasEmailAddress + ? ListTile( + leading: Icon(Icons.send), + title: Text(AppLocalization.of(context).email), + onTap: () => viewModel.onEntityAction( + context, invoice, EntityAction.emailInvoice), + ) + : Container(), + ListTile( + leading: Icon(Icons.picture_as_pdf), + title: Text(AppLocalization.of(context).pdf), + onTap: () => viewModel.onEntityAction( + context, invoice, EntityAction.pdf), + ), + Divider(), + user.canEditEntity(invoice) && !invoice.isActive + ? ListTile( + leading: Icon(Icons.restore), + title: Text(AppLocalization.of(context).restore), + onTap: () => viewModel.onEntityAction( + context, invoice, EntityAction.restore), + ) + : Container(), + user.canEditEntity(invoice) && invoice.isActive + ? ListTile( + leading: Icon(Icons.archive), + title: Text(AppLocalization.of(context).archive), + onTap: () => viewModel.onEntityAction( + context, invoice, EntityAction.archive), + ) + : Container(), + user.canEditEntity(invoice) && !invoice.isDeleted + ? ListTile( + leading: Icon(Icons.delete), + title: Text(AppLocalization.of(context).delete), + onTap: () => viewModel.onEntityAction( + context, invoice, EntityAction.delete), + ) + : Container(), + ])); if (message != null) { Scaffold.of(context).showSnackBar(SnackBar( content: SnackBarRow( - message: message, - ))); + message: message, + ))); } } - Widget _buildListView(BuildContext context) { - return RefreshIndicator( - onRefresh: () => viewModel.onRefreshed(context), - child: ListView.builder( - itemCount: viewModel.quoteList.length, - itemBuilder: (BuildContext context, index) { - final quoteId = viewModel.quoteList[index]; - final quote = viewModel.quoteMap[quoteId]; - return Column(children: [ - QuoteListItem( - user: viewModel.user, - filter: viewModel.filter, - quote: quote, - client: viewModel.clientMap[quote.clientId], - onDismissed: (DismissDirection direction) => - viewModel.onDismissed(context, quote, direction), - onTap: () => viewModel.onQuoteTap(context, quote), - onLongPress: () => _showMenu(context, quote), + @override + Widget build(BuildContext context) { + final localization = AppLocalization.of(context); + final listState = viewModel.listState; + final filteredClientId = listState.filterClientId; + final filteredClient = + filteredClientId != null ? viewModel.clientMap[filteredClientId] : null; + + return Column( + children: [ + filteredClient != null + ? Material( + color: Colors.orangeAccent, + elevation: 6.0, + child: InkWell( + onTap: () => viewModel.onViewClientFilterPressed(context), + child: Row( + children: [ + SizedBox(width: 18.0), + Expanded( + child: Text( + localization.clientsInvoices.replaceFirst( + ':client', filteredClient.displayName), + style: TextStyle( + color: Colors.white, + fontSize: 16.0, + ), + ), + ), + IconButton( + icon: Icon( + Icons.close, + color: Colors.white, + ), + onPressed: () => viewModel.onClearClientFilterPressed(), + ) + ], + ), + ), + ) + : Container(), + Expanded( + child: !viewModel.isLoaded + ? LoadingIndicator() + : RefreshIndicator( + onRefresh: () => viewModel.onRefreshed(context), + child: viewModel.invoiceList.isEmpty + ? Opacity( + opacity: 0.5, + child: Center( + child: Text( + AppLocalization.of(context).noRecordsFound, + style: TextStyle( + fontSize: 18.0, + ), + ), ), - Divider( - height: 1.0, - ), - ]); - }), + ) + : ListView.builder( + shrinkWrap: true, + itemCount: viewModel.invoiceList.length, + itemBuilder: (BuildContext context, index) { + final invoiceId = viewModel.invoiceList[index]; + final invoice = viewModel.invoiceMap[invoiceId]; + final client = + viewModel.clientMap[invoice.clientId]; + return Column( + children: [ + InvoiceListItem( + user: viewModel.user, + filter: viewModel.filter, + invoice: invoice, + client: viewModel.clientMap[invoice.clientId], + onDismissed: (DismissDirection direction) => + viewModel.onDismissed( + context, invoice, direction), + onTap: () => + viewModel.onInvoiceTap(context, invoice), + onLongPress: () => + _showMenu(context, invoice, client), + ), + Divider( + height: 1.0, + ), + ], + ); + }, + ), + ), + ), + ], ); } } diff --git a/lib/ui/quote/quote_list_item.dart b/lib/ui/quote/quote_list_item.dart index d15efe57d..cdf35e93d 100644 --- a/lib/ui/quote/quote_list_item.dart +++ b/lib/ui/quote/quote_list_item.dart @@ -12,7 +12,7 @@ class QuoteListItem extends StatelessWidget { final DismissDirectionCallback onDismissed; final GestureTapCallback onTap; final GestureTapCallback onLongPress; - final QuoteEntity quote; + final InvoiceEntity invoice; final ClientEntity client; final String filter; @@ -21,7 +21,7 @@ class QuoteListItem extends StatelessWidget { @required this.onDismissed, @required this.onTap, @required this.onLongPress, - @required this.quote, + @required this.invoice, @required this.client, @required this.filter, }); @@ -30,12 +30,13 @@ class QuoteListItem extends StatelessWidget { Widget build(BuildContext context) { final localization = AppLocalization.of(context); final filterMatch = filter != null && filter.isNotEmpty - ? (quote.matchesFilterValue(filter) ?? client.matchesFilterValue(filter)) + ? (invoice.matchesFilterValue(filter) ?? + client.matchesFilterValue(filter)) : null; return DismissibleEntity( user: user, - entity: quote, + entity: invoice, onDismissed: onDismissed, child: ListTile( onTap: onTap, @@ -51,8 +52,8 @@ class QuoteListItem extends StatelessWidget { ), ), Text( - formatNumber(quote.amount, context, - clientId: quote.clientId), + formatNumber(invoice.amount, context, + clientId: invoice.clientId), style: Theme.of(context).textTheme.title), ], ), @@ -64,25 +65,29 @@ class QuoteListItem extends StatelessWidget { children: [ Expanded( child: filterMatch == null - ? Text(quote.quoteNumber) + ? Text(invoice.invoiceNumber) : Text( - filterMatch, - maxLines: 3, - overflow: TextOverflow.ellipsis, - ), + filterMatch, + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), ), - Text(quote.isPastDue ? localization.pastDue : localization.lookup('invoice_status_${quote.quoteStatusId}'), + Text( + invoice.isPastDue + ? localization.pastDue + : localization.lookup( + 'invoice_status_${invoice.invoiceStatusId}'), style: TextStyle( - color: - quote.isPastDue ? Colors.red : InvoiceStatusColors.colors[quote.quoteStatusId], + color: invoice.isPastDue + ? Colors.red + : InvoiceStatusColors.colors[invoice.invoiceStatusId], )), ], ), - EntityStateLabel(quote), + EntityStateLabel(invoice), ], ), ), ); } } - diff --git a/lib/ui/quote/quote_list_vm.dart b/lib/ui/quote/quote_list_vm.dart index 4cd839ebc..2250137c0 100644 --- a/lib/ui/quote/quote_list_vm.dart +++ b/lib/ui/quote/quote_list_vm.dart @@ -1,28 +1,35 @@ import 'dart:async'; -import 'package:redux/redux.dart'; +import 'package:built_collection/built_collection.dart'; +import 'package:invoiceninja_flutter/redux/client/client_actions.dart'; +import 'package:invoiceninja_flutter/redux/quote/quote_selectors.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/ui/list_ui_state.dart'; +import 'package:invoiceninja_flutter/ui/quote/quote_list.dart'; import 'package:invoiceninja_flutter/utils/completers.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; -import 'package:invoiceninja_flutter/redux/quote/quote_selectors.dart'; +import 'package:invoiceninja_flutter/utils/pdf.dart'; +import 'package:redux/redux.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; -import 'package:invoiceninja_flutter/ui/quote/quote_list.dart'; +import 'package:invoiceninja_flutter/ui/invoice/invoice_list.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; -import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart'; +import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart'; class QuoteListBuilder extends StatelessWidget { + static const String route = '/invoices/edit'; + const QuoteListBuilder({Key key}) : super(key: key); @override Widget build(BuildContext context) { return StoreConnector( + //rebuildOnChange: true, converter: QuoteListVM.fromStore, - builder: (context, viewModel) { + builder: (context, vm) { return QuoteList( - viewModel: viewModel, + viewModel: vm, ); }, ); @@ -31,28 +38,34 @@ class QuoteListBuilder extends StatelessWidget { class QuoteListVM { final UserEntity user; - final List quoteList; - final BuiltMap quoteMap; + final ListUIState listState; + final List invoiceList; + final BuiltMap invoiceMap; final BuiltMap clientMap; final String filter; final bool isLoading; final bool isLoaded; - final Function(BuildContext, QuoteEntity) onQuoteTap; - final Function(BuildContext, QuoteEntity, DismissDirection) onDismissed; + final Function(BuildContext, InvoiceEntity) onInvoiceTap; + final Function(BuildContext, InvoiceEntity, DismissDirection) onDismissed; final Function(BuildContext) onRefreshed; - final Function(BuildContext, QuoteEntity, EntityAction) onEntityAction; + final Function onClearClientFilterPressed; + final Function(BuildContext) onViewClientFilterPressed; + final Function(BuildContext, InvoiceEntity, EntityAction) onEntityAction; QuoteListVM({ @required this.user, - @required this.quoteList, - @required this.quoteMap, + @required this.listState, + @required this.invoiceList, + @required this.invoiceMap, @required this.clientMap, - @required this.filter, @required this.isLoading, @required this.isLoaded, - @required this.onQuoteTap, + @required this.filter, + @required this.onInvoiceTap, @required this.onDismissed, @required this.onRefreshed, + @required this.onClearClientFilterPressed, + @required this.onViewClientFilterPressed, @required this.onEntityAction, }); @@ -63,7 +76,7 @@ class QuoteListVM { } final completer = snackBarCompleter( context, AppLocalization.of(context).refreshComplete); - store.dispatch(LoadQuotes(completer: completer, force: true)); + store.dispatch(LoadInvoices(completer: completer, force: true)); return completer.future; } @@ -71,66 +84,98 @@ class QuoteListVM { return QuoteListVM( user: state.user, - quoteList: memoizedFilteredQuoteList(state.quoteState.map, - state.quoteState.list, state.quoteListState), - quoteMap: state.quoteState.map, + listState: state.invoiceListState, + invoiceList: memoizedFilteredQuoteList( + state.invoiceState.map, + state.invoiceState.list, + state.clientState.map, + state.invoiceListState), + invoiceMap: state.invoiceState.map, clientMap: state.clientState.map, isLoading: state.isLoading, - isLoaded: state.quoteState.isLoaded, - filter: state.quoteUIState.listUIState.filter, - onQuoteTap: (context, quote) { - store.dispatch(EditQuote(quote: quote, context: context)); + isLoaded: state.invoiceState.isLoaded && state.clientState.isLoaded, + filter: state.invoiceListState.filter, + onInvoiceTap: (context, invoice) { + store.dispatch(ViewInvoice(invoiceId: invoice.id, context: context)); }, - onEntityAction: (context, quote, action) { + onRefreshed: (context) => _handleRefresh(context), + onClearClientFilterPressed: () => + store.dispatch(FilterInvoicesByClient()), + onViewClientFilterPressed: (BuildContext context) => store.dispatch( + ViewClient( + clientId: state.invoiceListState.filterClientId, + context: context)), + onEntityAction: (context, invoice, action) { + final localization = AppLocalization.of(context); switch (action) { + case EntityAction.pdf: + Navigator.of(context).pop(); + viewPdf(invoice, context); + break; + case EntityAction.markSent: + store.dispatch(MarkSentInvoiceRequest( + popCompleter( + context, localization.markedInvoiceAsSent), + invoice.id)); + break; + case EntityAction.emailInvoice: + store.dispatch(ShowEmailInvoice( + completer: popCompleter( + context, localization.emailedInvoice), + invoice: invoice, + context: context)); + break; case EntityAction.clone: Navigator.of(context).pop(); store.dispatch( - EditQuote(context: context, quote: quote.clone)); + EditInvoice(context: context, invoice: invoice.clone)); break; case EntityAction.restore: - store.dispatch(RestoreQuoteRequest( + store.dispatch(RestoreInvoiceRequest( popCompleter( - context, AppLocalization.of(context).restoredQuote), - quote.id)); + context, localization.restoredInvoice), + invoice.id)); break; case EntityAction.archive: - store.dispatch(ArchiveQuoteRequest( + store.dispatch(ArchiveInvoiceRequest( popCompleter( - context, AppLocalization.of(context).archivedQuote), - quote.id)); + context, localization.archivedInvoice), + invoice.id)); break; case EntityAction.delete: - store.dispatch(DeleteQuoteRequest( + store.dispatch(DeleteInvoiceRequest( popCompleter( - context, AppLocalization.of(context).deletedQuote), - quote.id)); + context, localization.deletedInvoice), + invoice.id)); break; } }, - onRefreshed: (context) => _handleRefresh(context), - onDismissed: (BuildContext context, QuoteEntity quote, + onDismissed: (BuildContext context, InvoiceEntity invoice, DismissDirection direction) { final localization = AppLocalization.of(context); if (direction == DismissDirection.endToStart) { - if (quote.isDeleted || quote.isArchived) { - store.dispatch(RestoreQuoteRequest( - snackBarCompleter(context, localization.restoredQuote), - quote.id)); + if (invoice.isDeleted || invoice.isArchived) { + store.dispatch(RestoreInvoiceRequest( + snackBarCompleter( + context, localization.restoredInvoice), + invoice.id)); } else { - store.dispatch(ArchiveQuoteRequest( - snackBarCompleter(context, localization.archivedQuote), - quote.id)); + store.dispatch(ArchiveInvoiceRequest( + snackBarCompleter( + context, localization.archivedInvoice), + invoice.id)); } } else if (direction == DismissDirection.startToEnd) { - if (quote.isDeleted) { - store.dispatch(RestoreQuoteRequest( - snackBarCompleter(context, localization.restoredQuote), - quote.id)); + if (invoice.isDeleted) { + store.dispatch(RestoreInvoiceRequest( + snackBarCompleter( + context, localization.restoredInvoice), + invoice.id)); } else { - store.dispatch(DeleteQuoteRequest( - snackBarCompleter(context, localization.deletedQuote), - quote.id)); + store.dispatch(DeleteInvoiceRequest( + snackBarCompleter( + context, localization.deletedInvoice), + invoice.id)); } } }); diff --git a/lib/ui/quote/quote_screen.dart b/lib/ui/quote/quote_screen.dart index 65942c6ad..3f3add989 100644 --- a/lib/ui/quote/quote_screen.dart +++ b/lib/ui/quote/quote_screen.dart @@ -53,9 +53,9 @@ class QuoteScreen extends StatelessWidget { onSelectedCustom2: (value) => store.dispatch(FilterQuotesByCustom2(value)), sortFields: [ - QuoteFields.quoteNumber, - QuoteFields.quoteDate, - QuoteFields.updatedAt, + InvoiceFields.invoiceNumber, + InvoiceFields.invoiceDate, + InvoiceFields.updatedAt, ], onSelectedState: (EntityState state, value) { store.dispatch(FilterQuotesByState(state)); @@ -67,7 +67,7 @@ class QuoteScreen extends StatelessWidget { backgroundColor: Theme.of(context).primaryColorDark, onPressed: () { store.dispatch( - EditQuote(quote: QuoteEntity(), context: context)); + EditQuote(quote: InvoiceEntity(), context: context)); }, child: Icon( Icons.add, diff --git a/lib/ui/quote/view/quote_view_vm.dart b/lib/ui/quote/view/quote_view_vm.dart index d2af63a3a..ecdca4240 100644 --- a/lib/ui/quote/view/quote_view_vm.dart +++ b/lib/ui/quote/view/quote_view_vm.dart @@ -16,7 +16,7 @@ import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart'; class QuoteViewScreen extends StatelessWidget { - static const String route = '/invoice/view'; + static const String route = '/quote/view'; const QuoteViewScreen({Key key}) : super(key: key); @@ -38,7 +38,7 @@ class QuoteViewScreen extends StatelessWidget { class QuoteViewVM { final CompanyEntity company; - final QuoteEntity quote; + final InvoiceEntity quote; final ClientEntity client; final bool isSaving; final bool isDirty; @@ -80,13 +80,13 @@ class QuoteViewVM { quote: quote, client: client, onEditPressed: (BuildContext context, [InvoiceItemEntity invoiceItem]) { - final Completer completer = - new Completer(); + final Completer completer = + new Completer(); store.dispatch(EditQuote( quote: quote, context: context, completer: completer, - invoiceItem: invoiceItem)); + quoteItem: invoiceItem)); completer.future.then((invoice) { Scaffold.of(context).showSnackBar(SnackBar( content: SnackBarRow( @@ -104,39 +104,39 @@ class QuoteViewVM { final localization = AppLocalization.of(context); switch (action) { case EntityAction.pdf: - viewPdf(invoice, context); + viewPdf(quote, context); break; case EntityAction.markSent: store.dispatch(MarkSentQuoteRequest( snackBarCompleter(context, localization.markedQuoteAsSent), - invoice.id)); + quote.id)); break; - case EntityAction.emailQuote: + case EntityAction.emailInvoice: store.dispatch(ShowEmailQuote( completer: snackBarCompleter(context, localization.emailedQuote), - invoice: invoice, + quote: quote, context: context)); break; case EntityAction.archive: store.dispatch(ArchiveQuoteRequest( popCompleter(context, localization.archivedQuote), - invoice.id)); + quote.id)); break; case EntityAction.delete: store.dispatch(DeleteQuoteRequest( popCompleter(context, localization.deletedQuote), - invoice.id)); + quote.id)); break; case EntityAction.restore: store.dispatch(RestoreQuoteRequest( snackBarCompleter(context, localization.restoredQuote), - invoice.id)); + quote.id)); break; case EntityAction.clone: Navigator.of(context).pop(); store.dispatch( - EditQuote(context: context, invoice: invoice.clone)); + EditQuote(context: context, quote: quote.clone)); break; } }); @@ -146,7 +146,7 @@ class QuoteViewVM { bool operator ==(dynamic other) => client == other.client && company == other.company && - invoice == other.invoice && + quote == other.quote && isSaving == other.isSaving && isDirty == other.isDirty; @@ -154,7 +154,7 @@ class QuoteViewVM { int get hashCode => client.hashCode ^ company.hashCode ^ - invoice.hashCode ^ + quote.hashCode ^ isSaving.hashCode ^ isDirty.hashCode; } diff --git a/stubs/ui/stub/edit/stub_edit b/stubs/ui/stub/edit/stub_edit index a900202f5..4004aa659 100644 --- a/stubs/ui/stub/edit/stub_edit +++ b/stubs/ui/stub/edit/stub_edit @@ -1,8 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_redux_starter/ui/app/form_card.dart'; -import 'package:flutter_redux_starter/ui/stub/edit/stub_edit_vm.dart'; -import 'package:flutter_redux_starter/ui/app/save_icon_button.dart'; +import 'package:invoiceninja_flutter/ui/app/form_card.dart'; +import 'package:invoiceninja_flutter/ui/stub/edit/stub_edit_vm.dart'; +import 'package:invoiceninja_flutter/ui/app/buttons/refresh_icon_button.dart'; class StubEdit extends StatefulWidget { final StubEditVM viewModel; diff --git a/stubs/ui/stub/edit/stub_edit_vm b/stubs/ui/stub/edit/stub_edit_vm index 676285e1f..271c7c1f1 100644 --- a/stubs/ui/stub/edit/stub_edit_vm +++ b/stubs/ui/stub/edit/stub_edit_vm @@ -2,14 +2,14 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_redux/flutter_redux.dart'; -import 'package:flutter_redux_starter/redux/ui/ui_actions.dart'; -import 'package:flutter_redux_starter/ui/stub/stub_screen.dart'; +import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; +import 'package:invoiceninja_flutter/ui/stub/stub_screen.dart'; import 'package:redux/redux.dart'; -import 'package:flutter_redux_starter/redux/stub/stub_actions.dart'; -import 'package:flutter_redux_starter/data/models/stub_model.dart'; -import 'package:flutter_redux_starter/ui/stub/edit/stub_edit.dart'; -import 'package:flutter_redux_starter/redux/app/app_state.dart'; -import 'package:flutter_redux_starter/ui/app/icon_message.dart'; +import 'package:invoiceninja_flutter/redux/stub/stub_actions.dart'; +import 'package:invoiceninja_flutter/data/models/stub_model.dart'; +import 'package:invoiceninja_flutter/ui/stub/edit/stub_edit.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/ui/app/icon_message.dart'; class StubEditScreen extends StatelessWidget { static final String route = '/stub/edit';