This commit is contained in:
Hillel Coren 2020-10-22 15:24:46 +03:00
parent 4b0398a040
commit 16921e4e6e
12 changed files with 131 additions and 128 deletions

View File

@ -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

View File

@ -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

View File

@ -137,6 +137,9 @@ class _$VendorEntitySerializer implements StructuredSerializer<VendorEntity> {
'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<VendorEntity> {
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,

View File

@ -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: <Widget>[
Icon(
Icons.info_outline,
iconData ?? Icons.info_outline,
size: 18.0,
color: Colors.white,
),

View File

@ -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()
],
],
);
}

View File

@ -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<ExpenseView>
@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<ExpenseView>
Tab(
text: localization.overview,
),
Tab(
text: localization.details,
),
Tab(
text: expense.documents.isEmpty
? localization.documents
@ -73,10 +69,6 @@ class _ExpenseViewState extends State<ExpenseView>
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(

View File

@ -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<ExpenseViewDetails> {
@override
Widget build(BuildContext context) {
final state = StoreProvider.of<AppState>(context).state;
final localization = AppLocalization.of(context);
final expense = widget.expense;
List<Widget> _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 = <String, String>{
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 = <Widget>[
FieldGrid(fields),
Divider(
height: 1.0,
),
if (expense.publicNotes != null && expense.publicNotes.isNotEmpty)
IconMessage(expense.publicNotes),
];
return listTiles;
}
return ListView(
children: _buildDetailsList(),
);
}
}

View File

@ -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<Widget> _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 = <String, String>{
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 = <Widget>[
FieldGrid(fields),
];
return listTiles;
}
return ListView(
children: <Widget>[
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()
],
],
);
}

View File

@ -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,
);

View File

@ -88,6 +88,10 @@ class _ProjectOverviewState extends State<ProjectOverview> {
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<ProjectOverview> {
),
];
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;
}

View File

@ -39,7 +39,7 @@ class VendorEditNotesState extends State<VendorEditNotes> {
.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<VendorEditNotes> {
_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);

View File

@ -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()
],
],
);
}