diff --git a/lib/data/models/expense_model.dart b/lib/data/models/expense_model.dart index 4a1645fc1..c07bd4ba9 100644 --- a/lib/data/models/expense_model.dart +++ b/lib/data/models/expense_model.dart @@ -441,7 +441,8 @@ abstract class ExpenseEntity extends Object } bool isBetween(String startDate, String endDate) { - return startDate.compareTo(date) <= 0 && endDate.compareTo(date) >= 0; + return (startDate ?? '').compareTo(date ?? '') <= 0 && + (endDate ?? '').compareTo(date ?? '') >= 0; } @override diff --git a/lib/data/models/vendor_model.dart b/lib/data/models/vendor_model.dart index 12b646eb4..a664813ea 100644 --- a/lib/data/models/vendor_model.dart +++ b/lib/data/models/vendor_model.dart @@ -55,6 +55,7 @@ class VendorFields { static const String countryId = 'country_id'; static const String phone = 'phone'; static const String privateNotes = 'private_notes'; + static const String publicNotes = 'public_notes'; static const String website = 'website'; static const String vatNumber = 'vat_number'; static const String idNumber = 'id_number'; @@ -86,6 +87,7 @@ abstract class VendorEntity extends Object countryId: '', phone: '', privateNotes: '', + publicNotes: '', website: '', vatNumber: '', idNumber: '', @@ -147,6 +149,9 @@ abstract class VendorEntity extends Object @BuiltValueField(wireName: 'private_notes') String get privateNotes; + @BuiltValueField(wireName: 'public_notes') + String get publicNotes; + String get website; @nullable // TODO remove this diff --git a/lib/data/models/vendor_model.g.dart b/lib/data/models/vendor_model.g.dart index 0198fd3a3..6a36efff0 100644 --- a/lib/data/models/vendor_model.g.dart +++ b/lib/data/models/vendor_model.g.dart @@ -137,6 +137,9 @@ class _$VendorEntitySerializer implements StructuredSerializer { 'private_notes', serializers.serialize(object.privateNotes, specifiedType: const FullType(String)), + 'public_notes', + serializers.serialize(object.publicNotes, + specifiedType: const FullType(String)), 'website', serializers.serialize(object.website, specifiedType: const FullType(String)), @@ -264,6 +267,10 @@ class _$VendorEntitySerializer implements StructuredSerializer { result.privateNotes = serializers.deserialize(value, specifiedType: const FullType(String)) as String; break; + case 'public_notes': + result.publicNotes = serializers.deserialize(value, + specifiedType: const FullType(String)) as String; + break; case 'website': result.website = serializers.deserialize(value, specifiedType: const FullType(String)) as String; @@ -702,6 +709,8 @@ class _$VendorEntity extends VendorEntity { @override final String privateNotes; @override + final String publicNotes; + @override final String website; @override final String number; @@ -753,6 +762,7 @@ class _$VendorEntity extends VendorEntity { this.countryId, this.phone, this.privateNotes, + this.publicNotes, this.website, this.number, this.vatNumber, @@ -800,6 +810,9 @@ class _$VendorEntity extends VendorEntity { if (privateNotes == null) { throw new BuiltValueNullFieldError('VendorEntity', 'privateNotes'); } + if (publicNotes == null) { + throw new BuiltValueNullFieldError('VendorEntity', 'publicNotes'); + } if (website == null) { throw new BuiltValueNullFieldError('VendorEntity', 'website'); } @@ -861,6 +874,7 @@ class _$VendorEntity extends VendorEntity { countryId == other.countryId && phone == other.phone && privateNotes == other.privateNotes && + publicNotes == other.publicNotes && website == other.website && number == other.number && vatNumber == other.vatNumber && @@ -903,7 +917,7 @@ class _$VendorEntity extends VendorEntity { $jc( $jc( $jc( - $jc($jc($jc($jc($jc($jc($jc($jc($jc($jc(0, name.hashCode), address1.hashCode), address2.hashCode), city.hashCode), state.hashCode), postalCode.hashCode), countryId.hashCode), phone.hashCode), privateNotes.hashCode), + $jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc(0, name.hashCode), address1.hashCode), address2.hashCode), city.hashCode), state.hashCode), postalCode.hashCode), countryId.hashCode), phone.hashCode), privateNotes.hashCode), publicNotes.hashCode), website.hashCode), number.hashCode), vatNumber.hashCode), @@ -937,6 +951,7 @@ class _$VendorEntity extends VendorEntity { ..add('countryId', countryId) ..add('phone', phone) ..add('privateNotes', privateNotes) + ..add('publicNotes', publicNotes) ..add('website', website) ..add('number', number) ..add('vatNumber', vatNumber) @@ -1000,6 +1015,10 @@ class VendorEntityBuilder String get privateNotes => _$this._privateNotes; set privateNotes(String privateNotes) => _$this._privateNotes = privateNotes; + String _publicNotes; + String get publicNotes => _$this._publicNotes; + set publicNotes(String publicNotes) => _$this._publicNotes = publicNotes; + String _website; String get website => _$this._website; set website(String website) => _$this._website = website; @@ -1095,6 +1114,7 @@ class VendorEntityBuilder _countryId = _$v.countryId; _phone = _$v.phone; _privateNotes = _$v.privateNotes; + _publicNotes = _$v.publicNotes; _website = _$v.website; _number = _$v.number; _vatNumber = _$v.vatNumber; @@ -1147,6 +1167,7 @@ class VendorEntityBuilder countryId: countryId, phone: phone, privateNotes: privateNotes, + publicNotes: publicNotes, website: website, number: number, vatNumber: vatNumber, diff --git a/lib/ui/app/icon_message.dart b/lib/ui/app/icon_message.dart index 1b9622f47..2057be2e5 100644 --- a/lib/ui/app/icon_message.dart +++ b/lib/ui/app/icon_message.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; class IconMessage extends StatelessWidget { - const IconMessage(this.text); + const IconMessage(this.text, {this.iconData}); final String text; + final IconData iconData; @override Widget build(BuildContext context) { @@ -13,7 +14,7 @@ class IconMessage extends StatelessWidget { child: Row( children: [ Icon( - Icons.info_outline, + iconData ?? Icons.info_outline, size: 18.0, color: Colors.white, ), diff --git a/lib/ui/client/view/client_view_overview.dart b/lib/ui/client/view/client_view_overview.dart index 83d60dbe0..dfb8cb93b 100644 --- a/lib/ui/client/view/client_view_overview.dart +++ b/lib/ui/client/view/client_view_overview.dart @@ -119,9 +119,10 @@ class ClientOverview extends StatelessWidget { formatNumber(client.balance, context, clientId: client.id), ), ListDivider(), - client.privateNotes != null && client.privateNotes.isNotEmpty - ? IconMessage(client.privateNotes) - : Container(), + if ((client.privateNotes ?? '').isNotEmpty) ...[ + IconMessage(client.privateNotes, iconData: Icons.lock), + ListDivider() + ], if (client.hasGroup) EntityListTile( entity: group, @@ -244,6 +245,10 @@ class ClientOverview extends StatelessWidget { memoizedExpenseStatsForClient(client.id, state.expenseState.map) .present(localization.active, localization.archived), ), + if ((client.publicNotes ?? '').isNotEmpty) ...[ + IconMessage(client.publicNotes), + ListDivider() + ], ], ); } diff --git a/lib/ui/expense/view/expense_view.dart b/lib/ui/expense/view/expense_view.dart index bf37eb8d2..1b999fc55 100644 --- a/lib/ui/expense/view/expense_view.dart +++ b/lib/ui/expense/view/expense_view.dart @@ -2,7 +2,6 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:image_picker/image_picker.dart'; import 'package:invoiceninja_flutter/ui/app/view_scaffold.dart'; -import 'package:invoiceninja_flutter/ui/expense/view/expense_view_details.dart'; import 'package:invoiceninja_flutter/ui/expense/view/expense_view_documents.dart'; import 'package:invoiceninja_flutter/ui/expense/view/expense_view_vm.dart'; import 'package:invoiceninja_flutter/ui/expense/view/expense_view_overview.dart'; @@ -31,7 +30,7 @@ class _ExpenseViewState extends State @override void initState() { super.initState(); - _controller = TabController(vsync: this, length: 3); + _controller = TabController(vsync: this, length: 2); } @override @@ -55,9 +54,6 @@ class _ExpenseViewState extends State Tab( text: localization.overview, ), - Tab( - text: localization.details, - ), Tab( text: expense.documents.isEmpty ? localization.documents @@ -73,10 +69,6 @@ class _ExpenseViewState extends State onRefresh: () => viewModel.onRefreshed(context), child: ExpenseOverview(viewModel: viewModel), ), - RefreshIndicator( - onRefresh: () => viewModel.onRefreshed(context), - child: ExpenseViewDetails(expense: viewModel.expense), - ), RefreshIndicator( onRefresh: () => viewModel.onRefreshed(context), child: ExpenseViewDocuments( diff --git a/lib/ui/expense/view/expense_view_details.dart b/lib/ui/expense/view/expense_view_details.dart deleted file mode 100644 index 11b775da1..000000000 --- a/lib/ui/expense/view/expense_view_details.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_redux/flutter_redux.dart'; -import 'package:invoiceninja_flutter/data/models/models.dart'; -import 'package:invoiceninja_flutter/redux/app/app_state.dart'; -import 'package:invoiceninja_flutter/ui/app/FieldGrid.dart'; -import 'package:invoiceninja_flutter/ui/app/icon_message.dart'; -import 'package:invoiceninja_flutter/utils/formatting.dart'; -import 'package:invoiceninja_flutter/utils/localization.dart'; - -class ExpenseViewDetails extends StatefulWidget { - const ExpenseViewDetails({this.expense}); - - final ExpenseEntity expense; - - @override - _ExpenseViewDetailsState createState() => _ExpenseViewDetailsState(); -} - -class _ExpenseViewDetailsState extends State { - @override - Widget build(BuildContext context) { - final state = StoreProvider.of(context).state; - final localization = AppLocalization.of(context); - final expense = widget.expense; - - List _buildDetailsList() { - String tax = ''; - if (expense.taxName1.isNotEmpty) { - tax += formatNumber(expense.taxRate1, context, - formatNumberType: FormatNumberType.percent) + - ' ' + - expense.taxName1; - } - if (expense.taxName2.isNotEmpty) { - tax += ' ' + - formatNumber(expense.taxRate2, context, - formatNumberType: FormatNumberType.percent) + - ' ' + - expense.taxName2; - } - - final fields = { - localization.amount: formatNumber(expense.amount, context, - currencyId: expense.expenseCurrencyId), - localization.tax: tax, - localization.paymentType: - state.staticState.paymentTypeMap[expense.paymentTypeId]?.name, - localization.paymentDate: formatDate(expense.paymentDate, context), - localization.transactionReference: expense.transactionReference, - localization.exchangeRate: expense.isConverted - ? formatNumber(expense.exchangeRate, context, - formatNumberType: FormatNumberType.double) - : null, - localization.currency: expense.isConverted - ? state.staticState.currencyMap[expense.invoiceCurrencyId]?.name - : null, - }; - - final listTiles = [ - FieldGrid(fields), - Divider( - height: 1.0, - ), - if (expense.publicNotes != null && expense.publicNotes.isNotEmpty) - IconMessage(expense.publicNotes), - ]; - - return listTiles; - } - - return ListView( - children: _buildDetailsList(), - ); - } -} diff --git a/lib/ui/expense/view/expense_view_overview.dart b/lib/ui/expense/view/expense_view_overview.dart index 3b234571b..46503abeb 100644 --- a/lib/ui/expense/view/expense_view_overview.dart +++ b/lib/ui/expense/view/expense_view_overview.dart @@ -3,6 +3,7 @@ import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/ui/app/FieldGrid.dart'; import 'package:invoiceninja_flutter/ui/app/entities/entity_state_title.dart'; import 'package:invoiceninja_flutter/ui/app/entity_header.dart'; +import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart'; import 'package:invoiceninja_flutter/ui/expense/view/expense_view_vm.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:flutter/material.dart'; @@ -49,6 +50,44 @@ class ExpenseOverview extends StatelessWidget { value: expense.customValue2); } + List _buildDetailsList() { + String tax = ''; + if (expense.taxName1.isNotEmpty) { + tax += formatNumber(expense.taxRate1, context, + formatNumberType: FormatNumberType.percent) + + ' ' + + expense.taxName1; + } + if (expense.taxName2.isNotEmpty) { + tax += ' ' + + formatNumber(expense.taxRate2, context, + formatNumberType: FormatNumberType.percent) + + ' ' + + expense.taxName2; + } + + final fields = { + localization.tax: tax, + localization.paymentType: + state.staticState.paymentTypeMap[expense.paymentTypeId]?.name, + localization.paymentDate: formatDate(expense.paymentDate, context), + localization.transactionReference: expense.transactionReference, + localization.exchangeRate: expense.isConverted + ? formatNumber(expense.exchangeRate, context, + formatNumberType: FormatNumberType.double) + : null, + localization.currency: expense.isConverted + ? state.staticState.currencyMap[expense.invoiceCurrencyId]?.name + : null, + }; + + final listTiles = [ + FieldGrid(fields), + ]; + + return listTiles; + } + return ListView( children: [ expense.isConverted @@ -74,13 +113,12 @@ class ExpenseOverview extends StatelessWidget { value: formatNumber(expense.amountWithTax, context, currencyId: expense.expenseCurrencyId), ), - expense.privateNotes != null && expense.privateNotes.isNotEmpty - ? IconMessage(expense.privateNotes) - : Container(), + ListDivider(), + if ((expense.privateNotes ?? '').isNotEmpty) ...[ + IconMessage(expense.privateNotes, iconData: Icons.lock), + ], FieldGrid(fields), - Divider( - height: 1.0, - ), + ListDivider(), vendor == null ? SizedBox() : Material( @@ -137,12 +175,11 @@ class ExpenseOverview extends StatelessWidget { context, EntityType.invoice, true), ), ), - invoice == null - ? SizedBox() - : Container( - color: Theme.of(context).backgroundColor, - height: 12.0, - ), + ..._buildDetailsList(), + if ((expense.publicNotes ?? '').isNotEmpty) ...[ + IconMessage(expense.publicNotes), + ListDivider() + ], ], ); } diff --git a/lib/ui/invoice/view/invoice_view_overview.dart b/lib/ui/invoice/view/invoice_view_overview.dart index 00981ea20..e909c7e12 100644 --- a/lib/ui/invoice/view/invoice_view_overview.dart +++ b/lib/ui/invoice/view/invoice_view_overview.dart @@ -104,6 +104,16 @@ class InvoiceOverview extends StatelessWidget { ListDivider(), ]; + if ((invoice.privateNotes ?? '').isNotEmpty) { + widgets.addAll([ + IconMessage( + invoice.privateNotes, + iconData: Icons.lock, + ), + ListDivider(), + ]); + } + String dueDateField = InvoiceFields.dueDate; if (invoice.isQuote) { dueDateField = QuoteFields.validUntil; @@ -282,13 +292,6 @@ class InvoiceOverview extends StatelessWidget { FieldGrid(fields), ]); - if (invoice.privateNotes != null && invoice.privateNotes.isNotEmpty) { - widgets.addAll([ - IconMessage(invoice.privateNotes), - ListDivider(), - ]); - } - if (invoice.lineItems.isNotEmpty) { invoice.lineItems.forEach((invoiceItem) { widgets.addAll([ @@ -385,6 +388,13 @@ class InvoiceOverview extends StatelessWidget { widgets.add(surchargeRow(localization.total, invoice.partial != 0 ? invoice.partial : invoice.calculateTotal)); + if ((invoice.publicNotes ?? '').isNotEmpty) { + widgets.addAll([ + ListDivider(), + IconMessage(invoice.publicNotes), + ]); + } + return ListView( children: widgets, ); diff --git a/lib/ui/project/view/project_view_overview.dart b/lib/ui/project/view/project_view_overview.dart index 4766f3c3c..12321667c 100644 --- a/lib/ui/project/view/project_view_overview.dart +++ b/lib/ui/project/view/project_view_overview.dart @@ -88,6 +88,10 @@ class _ProjectOverviewState extends State { showSeconds: false), ), ListDivider(), + if ((project.privateNotes ?? '').isNotEmpty) ...[ + IconMessage(project.privateNotes, iconData: Icons.lock), + ListDivider() + ], EntityListTile( entity: client, isFilter: widget.isFilter, @@ -114,20 +118,17 @@ class _ProjectOverviewState extends State { ), ]; - if (project.privateNotes != null && project.privateNotes.isNotEmpty) { - widgets.addAll([ - IconMessage(project.privateNotes), - Container( - color: Theme.of(context).backgroundColor, - height: 12.0, - ), - ]); - } - widgets.addAll([ FieldGrid(fields), ]); + if ((project.publicNotes ?? '').isNotEmpty) { + widgets.addAll([ + IconMessage(project.publicNotes), + ListDivider() + ]); + } + return widgets; } diff --git a/lib/ui/vendor/edit/vendor_edit_notes.dart b/lib/ui/vendor/edit/vendor_edit_notes.dart index cf0a4b253..703b2b734 100644 --- a/lib/ui/vendor/edit/vendor_edit_notes.dart +++ b/lib/ui/vendor/edit/vendor_edit_notes.dart @@ -39,7 +39,7 @@ class VendorEditNotesState extends State { .forEach((dynamic controller) => controller.removeListener(_onChanged)); final vendor = widget.viewModel.vendor; - //_publicNotesController.text = vendor.publicNotes; + _publicNotesController.text = vendor.publicNotes; _privateNotesController.text = vendor.privateNotes; _controllers @@ -62,7 +62,7 @@ class VendorEditNotesState extends State { _debouncer.run(() { final viewModel = widget.viewModel; final vendor = viewModel.vendor.rebuild((b) => b - //..publicNotes = _publicNotesController.text + ..publicNotes = _publicNotesController.text ..privateNotes = _privateNotesController.text); if (vendor != viewModel.vendor) { viewModel.onChanged(vendor); diff --git a/lib/ui/vendor/view/vendor_view_overview.dart b/lib/ui/vendor/view/vendor_view_overview.dart index cdafaae23..4dc6a1da8 100644 --- a/lib/ui/vendor/view/vendor_view_overview.dart +++ b/lib/ui/vendor/view/vendor_view_overview.dart @@ -7,6 +7,7 @@ import 'package:invoiceninja_flutter/redux/vendor/vendor_selectors.dart'; import 'package:invoiceninja_flutter/ui/app/FieldGrid.dart'; import 'package:invoiceninja_flutter/ui/app/entities/entity_list_tile.dart'; import 'package:invoiceninja_flutter/ui/app/entity_header.dart'; +import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart'; import 'package:invoiceninja_flutter/ui/vendor/view/vendor_view_vm.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:flutter/material.dart'; @@ -64,13 +65,13 @@ class VendorOverview extends StatelessWidget { context, currencyId: vendor.currencyId ?? company.currencyId), ), - vendor.privateNotes != null && vendor.privateNotes.isNotEmpty - ? IconMessage(vendor.privateNotes) - : Container(), + ListDivider(), + if ((vendor.privateNotes ?? '').isNotEmpty) ...[ + IconMessage(vendor.privateNotes, iconData: Icons.lock), + ListDivider() + ], FieldGrid(fields), - Divider( - height: 1.0, - ), + ListDivider(), EntitiesListTile( entity: vendor, title: localization.expenses, @@ -80,6 +81,10 @@ class VendorOverview extends StatelessWidget { memoizedExpenseStatsForVendor(vendor.id, state.expenseState.map) .present(localization.active, localization.archived), ), + if ((vendor.publicNotes ?? '').isNotEmpty) ...[ + IconMessage(vendor.publicNotes), + ListDivider() + ], ], ); }