This commit is contained in:
Hillel Coren 2018-09-25 18:19:26 +03:00
parent 751bfa62ff
commit ac56a46ed6
16 changed files with 86 additions and 45 deletions

View File

@ -56,9 +56,9 @@ abstract class ClientEntity extends Object
implements Built<ClientEntity, ClientEntityBuilder> { implements Built<ClientEntity, ClientEntityBuilder> {
static int counter = 0; static int counter = 0;
factory ClientEntity() { factory ClientEntity({int id}) {
return _$ClientEntity._( return _$ClientEntity._(
id: --ClientEntity.counter, id: id ?? --ClientEntity.counter,
name: '', name: '',
displayName: '', displayName: '',
balance: 0.0, balance: 0.0,

View File

@ -88,9 +88,9 @@ abstract class InvoiceEntity extends Object
implements Built<InvoiceEntity, InvoiceEntityBuilder> { implements Built<InvoiceEntity, InvoiceEntityBuilder> {
static int counter = 0; static int counter = 0;
factory InvoiceEntity({bool isQuote = false}) { factory InvoiceEntity({int id, bool isQuote = false}) {
return _$InvoiceEntity._( return _$InvoiceEntity._(
id: --InvoiceEntity.counter, id: id ?? --InvoiceEntity.counter,
amount: 0.0, amount: 0.0,
balance: 0.0, balance: 0.0,
clientId: 0, clientId: 0,
@ -448,7 +448,7 @@ abstract class InvoiceEntity extends Object
bool isBetween(String startDate, String endDate) { bool isBetween(String startDate, String endDate) {
return startDate.compareTo(invoiceDate) <= 0 && return startDate.compareTo(invoiceDate) <= 0 &&
endDate.compareTo(invoiceDate) >= 0; endDate.compareTo(invoiceDate) == 1;
} }
double get requestedAmount => partial > 0 ? partial : amount; double get requestedAmount => partial > 0 ? partial : amount;
@ -474,7 +474,7 @@ abstract class InvoiceEntity extends Object
String get invitationDownloadLink => invitations.first?.downloadLink; String get invitationDownloadLink => invitations.first?.downloadLink;
PaymentEntity createPayment(CompanyEntity company) { PaymentEntity createPayment(CompanyEntity company) {
return PaymentEntity(company).rebuild((b) => b return PaymentEntity(company: company).rebuild((b) => b
..invoiceId = id ..invoiceId = id
..clientId = clientId ..clientId = clientId
..amount = balance); ..amount = balance);

View File

@ -56,9 +56,9 @@ abstract class PaymentEntity extends Object
implements Built<PaymentEntity, PaymentEntityBuilder> { implements Built<PaymentEntity, PaymentEntityBuilder> {
static int counter = 0; static int counter = 0;
factory PaymentEntity([CompanyEntity company]) { factory PaymentEntity({int id, CompanyEntity company}) {
return _$PaymentEntity._( return _$PaymentEntity._(
id: --PaymentEntity.counter, id: id ?? --PaymentEntity.counter,
amount: 0.0, amount: 0.0,
transactionReference: '', transactionReference: '',
paymentDate: convertDateTimeToSqlDate(), paymentDate: convertDateTimeToSqlDate(),

View File

@ -30,7 +30,8 @@ List<ChartMoneyData> chartOutstandingInvoices({
final Map<String, double> totals = {}; final Map<String, double> totals = {};
invoiceMap.forEach((int, invoice) { invoiceMap.forEach((int, invoice) {
final client = clientMap[invoice.clientId] ?? ClientEntity(); final client =
clientMap[invoice.clientId] ?? ClientEntity(id: invoice.clientId);
final currencyId = final currencyId =
client.currencyId > 0 ? client.currencyId : company.currencyId; client.currencyId > 0 ? client.currencyId : company.currencyId;
@ -39,7 +40,8 @@ List<ChartMoneyData> chartOutstandingInvoices({
invoice.isQuote || invoice.isQuote ||
invoice.isRecurring) { invoice.isRecurring) {
// skip it // skip it
} else if (!invoice.isBetween(settings.startDate(company), settings.endDate(company))) { } else if (!invoice.isBetween(
settings.startDate(company), settings.endDate(company))) {
// skip it // skip it
} else if (settings.currencyId > 0 && settings.currencyId != currencyId) { } else if (settings.currencyId > 0 && settings.currencyId != currencyId) {
// skip it // skip it
@ -55,7 +57,7 @@ List<ChartMoneyData> chartOutstandingInvoices({
var date = DateTime.parse(settings.startDate(company)); var date = DateTime.parse(settings.startDate(company));
final endDate = DateTime.parse(settings.endDate(company)); final endDate = DateTime.parse(settings.endDate(company));
while (! date.isAfter(endDate)) { while (!date.isAfter(endDate)) {
final key = convertDateTimeToSqlDate(date); final key = convertDateTimeToSqlDate(date);
if (totals.containsKey(key)) { if (totals.containsKey(key)) {
data.add(ChartMoneyData(date, totals[key])); data.add(ChartMoneyData(date, totals[key]));
@ -84,14 +86,17 @@ List<ChartMoneyData> chartPayments(
final Map<String, double> totals = {}; final Map<String, double> totals = {};
paymentMap.forEach((int, payment) { paymentMap.forEach((int, payment) {
final invoice = invoiceMap[payment.invoiceId] ?? InvoiceEntity(); final invoice =
final client = clientMap[invoice.clientId] ?? ClientEntity(); invoiceMap[payment.invoiceId] ?? InvoiceEntity(id: payment.invoiceId);
final client =
clientMap[invoice.clientId] ?? ClientEntity(id: invoice.clientId);
final currencyId = final currencyId =
client.currencyId > 0 ? client.currencyId : company.currencyId; client.currencyId > 0 ? client.currencyId : company.currencyId;
if (payment.isDeleted) { if (payment.isDeleted) {
// skip it // skip it
} else if (!payment.isBetween(settings.startDate(company), settings.endDate(company))) { } else if (!payment.isBetween(
settings.startDate(company), settings.endDate(company))) {
// skip it // skip it
} else if (settings.currencyId > 0 && settings.currencyId != currencyId) { } else if (settings.currencyId > 0 && settings.currencyId != currencyId) {
// skip it // skip it
@ -107,7 +112,7 @@ List<ChartMoneyData> chartPayments(
var date = DateTime.parse(settings.startDate(company)); var date = DateTime.parse(settings.startDate(company));
final endDate = DateTime.parse(settings.endDate(company)); final endDate = DateTime.parse(settings.endDate(company));
while (! date.isAfter(endDate)) { while (!date.isAfter(endDate)) {
final key = convertDateTimeToSqlDate(date); final key = convertDateTimeToSqlDate(date);
if (totals.containsKey(key)) { if (totals.containsKey(key)) {
data.add(ChartMoneyData(date, totals[key])); data.add(ChartMoneyData(date, totals[key]));

View File

@ -29,7 +29,7 @@ List<int> dropdownInvoiceSelector(BuiltMap<int, InvoiceEntity> invoiceMap,
ClientEntity invoiceClientSelector( ClientEntity invoiceClientSelector(
InvoiceEntity invoice, BuiltMap<int, ClientEntity> clientMap) { InvoiceEntity invoice, BuiltMap<int, ClientEntity> clientMap) {
return clientMap[invoice.clientId] ?? ClientEntity(); return clientMap[invoice.clientId] ?? ClientEntity(id: invoice.clientId);
} }
var memoizedFilteredInvoiceList = memo4( var memoizedFilteredInvoiceList = memo4(
@ -47,7 +47,7 @@ List<int> filteredInvoicesSelector(
ListUIState invoiceListState) { ListUIState invoiceListState) {
final list = invoiceList.where((invoiceId) { final list = invoiceList.where((invoiceId) {
final invoice = invoiceMap[invoiceId]; final invoice = invoiceMap[invoiceId];
final client = clientMap[invoice.clientId] ?? ClientEntity(); final client = clientMap[invoice.clientId] ?? ClientEntity(id: invoice.clientId);
if (client == null || !client.isActive) { if (client == null || !client.isActive) {
return false; return false;
} }

View File

@ -17,13 +17,16 @@ List<PaymentEntity> paymentsByInvoiceSelector(int invoiceId,
} }
InvoiceEntity paymentInvoiceSelector(int paymentId, AppState state) { InvoiceEntity paymentInvoiceSelector(int paymentId, AppState state) {
final payment = state.paymentState.map[paymentId] ?? PaymentEntity(); final payment =
return state.invoiceState.map[payment.invoiceId] ?? InvoiceEntity(); state.paymentState.map[paymentId] ?? PaymentEntity(id: paymentId);
return state.invoiceState.map[payment.invoiceId] ??
InvoiceEntity(id: payment.invoiceId);
} }
ClientEntity paymentClientSelector(int paymentId, AppState state) { ClientEntity paymentClientSelector(int paymentId, AppState state) {
final invoice = paymentInvoiceSelector(paymentId, state); final invoice = paymentInvoiceSelector(paymentId, state);
return state.clientState.map[invoice.clientId] ?? ClientEntity(); return state.clientState.map[invoice.clientId] ??
ClientEntity(id: invoice.clientId);
} }
var memoizedDropdownPaymentList = memo2( var memoizedDropdownPaymentList = memo2(

View File

@ -44,7 +44,7 @@ abstract class PaymentUIState extends Object with EntityUIState implements Built
factory PaymentUIState(CompanyEntity company) { factory PaymentUIState(CompanyEntity company) {
return _$PaymentUIState._( return _$PaymentUIState._(
listUIState: ListUIState(PaymentFields.paymentDate), listUIState: ListUIState(PaymentFields.paymentDate),
editing: PaymentEntity(company), editing: PaymentEntity(company: company),
selectedId: 0, selectedId: 0,
); );
} }

View File

@ -12,13 +12,14 @@ import 'package:invoiceninja_flutter/ui/app/app_drawer_vm.dart';
import 'package:invoiceninja_flutter/ui/settings/settings_screen.dart'; import 'package:invoiceninja_flutter/ui/settings/settings_screen.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart';
import 'package:invoiceninja_flutter/utils/platforms.dart';
import 'package:redux/redux.dart'; import 'package:redux/redux.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:cached_network_image/cached_network_image.dart'; import 'package:cached_network_image/cached_network_image.dart';
// STARTER: import - do not remove comment // STARTER: import - do not remove comment
import 'package:invoiceninja_flutter/redux/payment/payment_actions.dart'; import 'package:invoiceninja_flutter/redux/payment/payment_actions.dart';
import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart'; import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart';
import 'package:url_launcher/url_launcher.dart';
class AppDrawer extends StatelessWidget { class AppDrawer extends StatelessWidget {
final AppDrawerVM viewModel; final AppDrawerVM viewModel;
@ -83,8 +84,8 @@ class AppDrawer extends StatelessWidget {
final ThemeData themeData = Theme.of(context); final ThemeData themeData = Theme.of(context);
final TextStyle aboutTextStyle = themeData.textTheme.body2; final TextStyle aboutTextStyle = themeData.textTheme.body2;
//final TextStyle linkStyle = final TextStyle linkStyle =
//themeData.textTheme.body2.copyWith(color: themeData.accentColor); themeData.textTheme.body2.copyWith(color: themeData.accentColor);
return Drawer( return Drawer(
child: ListView( child: ListView(
@ -192,7 +193,7 @@ class AppDrawer extends StatelessWidget {
onCreateTap: () { onCreateTap: () {
navigator.pop(); navigator.pop();
store.dispatch(EditPayment( store.dispatch(EditPayment(
payment: PaymentEntity(company), context: context)); payment: PaymentEntity(company: company), context: context));
}, },
), ),
DrawerTile( DrawerTile(
@ -217,10 +218,26 @@ class AppDrawer extends StatelessWidget {
builder: (BuildContext context) => AlertDialog( builder: (BuildContext context) => AlertDialog(
semanticLabel: 'Task & Expenses', semanticLabel: 'Task & Expenses',
title: Text('Task & Expenses'), title: Text('Task & Expenses'),
content: Text( content: RichText(
'Thank for your patience while we work to implement these features.\n\n' + text: TextSpan(
'We hope to have them completed in the next few months.\n\n' children: <TextSpan>[
'Until then we\'ll continue to support the legacy mobile apps.'), TextSpan(
style: aboutTextStyle,
text:
localization.thanksForPatience + ' ',
),
_LinkTextSpan(
style: linkStyle,
url: getLegacyAppURL(context),
text: localization.legacyMobileApp,
),
TextSpan(
style: aboutTextStyle,
text: '.',
),
],
),
),
actions: <Widget>[ actions: <Widget>[
FlatButton( FlatButton(
child: Text(localization.ok.toUpperCase()), child: Text(localization.ok.toUpperCase()),

View File

@ -89,7 +89,7 @@ class ClientListVM {
break; break;
case EntityAction.payment: case EntityAction.payment:
store.dispatch(EditPayment( store.dispatch(EditPayment(
payment: PaymentEntity(state.selectedCompany) payment: PaymentEntity(company: state.selectedCompany)
.rebuild((b) => b.clientId = client.id), .rebuild((b) => b.clientId = client.id),
context: context)); context: context));
break; break;

View File

@ -94,7 +94,7 @@ class _ClientViewState extends State<ClientView>
onTap: () { onTap: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
store.dispatch(EditPayment( store.dispatch(EditPayment(
payment: PaymentEntity(company) payment: PaymentEntity(company: company)
.rebuild((b) => b.clientId = client.id), .rebuild((b) => b.clientId = client.id),
context: context)); context: context));
}, },

View File

@ -21,8 +21,8 @@ class InvoiceEmailScreen extends StatelessWidget {
final state = store.state; final state = store.state;
final invoiceId = state.uiState.invoiceUIState.selectedId; final invoiceId = state.uiState.invoiceUIState.selectedId;
final invoice = state.invoiceState.map[invoiceId]; final invoice = state.invoiceState.map[invoiceId];
final client = final client = state.clientState.map[invoice.clientId] ??
state.clientState.map[invoice.clientId] ?? ClientEntity(); ClientEntity(id: invoice.clientId);
if (client.areActivitiesStale) { if (client.areActivitiesStale) {
store.dispatch(LoadClient(clientId: client.id, loadActivities: true)); store.dispatch(LoadClient(clientId: client.id, loadActivities: true));
} }
@ -86,7 +86,8 @@ class EmailInvoiceVM extends EmailEntityVM {
isSaving: state.isSaving, isSaving: state.isSaving,
company: state.selectedCompany, company: state.selectedCompany,
invoice: invoice, invoice: invoice,
client: state.clientState.map[invoice.clientId] ?? ClientEntity(), client: state.clientState.map[invoice.clientId] ??
ClientEntity(id: invoice.clientId),
onSendPressed: (context, template, subject, body) => onSendPressed: (context, template, subject, body) =>
store.dispatch(EmailInvoiceRequest( store.dispatch(EmailInvoiceRequest(
completer: popCompleter(context, true), completer: popCompleter(context, true),

View File

@ -116,8 +116,8 @@ class InvoiceViewVM extends EntityViewVM {
factory InvoiceViewVM.fromStore(Store<AppState> store) { factory InvoiceViewVM.fromStore(Store<AppState> store) {
final state = store.state; final state = store.state;
final invoice = state.invoiceState.map[state.invoiceUIState.selectedId]; final invoice = state.invoiceState.map[state.invoiceUIState.selectedId];
final client = final client = store.state.clientState.map[invoice.clientId] ??
store.state.clientState.map[invoice.clientId] ?? ClientEntity(); ClientEntity(id: invoice.clientId);
Future<Null> _handleRefresh(BuildContext context) { Future<Null> _handleRefresh(BuildContext context) {
final completer = snackBarCompleter( final completer = snackBarCompleter(

View File

@ -68,7 +68,8 @@ class PaymentScreen extends StatelessWidget {
backgroundColor: Theme.of(context).primaryColorDark, backgroundColor: Theme.of(context).primaryColorDark,
onPressed: () { onPressed: () {
store.dispatch(EditPayment( store.dispatch(EditPayment(
payment: PaymentEntity(company), context: context)); payment: PaymentEntity(company: company),
context: context));
}, },
child: Icon( child: Icon(
Icons.add, Icons.add,

View File

@ -20,6 +20,9 @@ class AppLocalization {
static final Map<String, Map<String, String>> _localizedValues = { static final Map<String, Map<String, String>> _localizedValues = {
'en': { 'en': {
'thanks_for_patience':
'Thank for your patience while we work to implement these features.\n\nWe hope to have them completed in the next few months.\n\nUntil then we\'ll continue to support the',
'legacy_mobile_app': 'legacy mobile app',
'today': 'Today', 'today': 'Today',
'custom_range': 'Custom', 'custom_range': 'Custom',
'date_range': 'Date Range', 'date_range': 'Date Range',
@ -8344,6 +8347,12 @@ class AppLocalization {
}, },
}; };
String get thanksForPatience =>
_localizedValues[locale.toString()]['thanks_for_patience'];
String get legacyMobileApp =>
_localizedValues[locale.toString()]['legacy_mobile_app'];
String get today => _localizedValues[locale.toString()]['today']; String get today => _localizedValues[locale.toString()]['today'];
String get customRange => _localizedValues[locale.toString()]['custom_range']; String get customRange => _localizedValues[locale.toString()]['custom_range'];

View File

@ -1,11 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
String getMapURL(BuildContext context) { bool isAndroid(BuildContext context) =>
final bool iOS = Theme.of(context).platform == TargetPlatform.iOS; Theme.of(context).platform == TargetPlatform.android;
return iOS
? 'http://maps.apple.com/?address=' String getMapURL(BuildContext context) => isAndroid(context)
: 'https://maps.google.com/?q='; ? 'https://maps.google.com/?q='
} : 'http://maps.apple.com/?address=';
String getLegacyAppURL(BuildContext context) => isAndroid(context)
? 'https://play.google.com/store/apps/details?id=com.invoiceninja.invoiceninja'
: 'https://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=1220337560&mt=8';
String getPlatform(BuildContext context) => String getPlatform(BuildContext context) =>
Theme.of(context).platform == TargetPlatform.iOS ? 'ios' : 'android'; Theme.of(context).platform == TargetPlatform.iOS ? 'ios' : 'android';

View File

@ -15,7 +15,8 @@ String processTemplate(
final state = StoreProvider.of<AppState>(context).state; final state = StoreProvider.of<AppState>(context).state;
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context);
final company = state.selectedCompany; final company = state.selectedCompany;
final client = state.clientState.map[invoice.clientId] ?? ClientEntity(); final client = state.clientState.map[invoice.clientId] ??
ClientEntity(id: invoice.clientId);
final contact = client.contacts.first; final contact = client.contacts.first;
final String sampleButton = '[${localization.button}]'; final String sampleButton = '[${localization.button}]';