Added exchange rate
This commit is contained in:
parent
92a7d72d5f
commit
a10a2f90c4
|
|
@ -159,7 +159,7 @@ abstract class InvoiceEntity extends Object
|
||||||
reminder2Sent: '',
|
reminder2Sent: '',
|
||||||
reminder3Sent: '',
|
reminder3Sent: '',
|
||||||
reminderLastSent: '',
|
reminderLastSent: '',
|
||||||
exchangeRate: 0,
|
exchangeRate: 1,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -636,6 +636,8 @@ abstract class AppState implements Built<AppState, AppStateBuilder> {
|
||||||
//return '${clientState.map[clientUIState.selectedId].gatewayTokens}';
|
//return '${clientState.map[clientUIState.selectedId].gatewayTokens}';
|
||||||
//return 'gatewayId: ${companyGatewayState.map[companyGatewayUIState.selectedId].gatewayId}';
|
//return 'gatewayId: ${companyGatewayState.map[companyGatewayUIState.selectedId].gatewayId}';
|
||||||
//return 'Language Id: ${company.settings.languageId}';
|
//return 'Language Id: ${company.settings.languageId}';
|
||||||
|
//return 'Rates: ${staticState.currencyMap.keys.map((key) => 'Rate: ${staticState.currencyMap[key].exchangeRate}').join(',')}';
|
||||||
|
return 'Client ID: ${invoiceUIState?.editing?.clientId ?? 'NULL'}';
|
||||||
return '\n\nURL: ${authState.url}\nRoute: ${uiState.currentRoute}\nPrev: ${uiState.previousRoute}\nIs Loaded: ${isLoaded ? 'Yes' : 'No'}\nis Large: ${(company?.isLarge ?? false) ? 'Yes' : 'No'}\nCompany: $companyUpdated${userCompanyState.isStale ? ' [S]' : ''}\nStatic: $staticUpdated${staticState.isStale ? ' [S]' : ''}\n';
|
return '\n\nURL: ${authState.url}\nRoute: ${uiState.currentRoute}\nPrev: ${uiState.previousRoute}\nIs Loaded: ${isLoaded ? 'Yes' : 'No'}\nis Large: ${(company?.isLarge ?? false) ? 'Yes' : 'No'}\nCompany: $companyUpdated${userCompanyState.isStale ? ' [S]' : ''}\nStatic: $staticUpdated${staticState.isStale ? ' [S]' : ''}\n';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,14 @@ abstract class GroupState implements Built<GroupState, GroupStateBuilder> {
|
||||||
@memoized
|
@memoized
|
||||||
int get hashCode;
|
int get hashCode;
|
||||||
|
|
||||||
|
GroupEntity get(String groupId) {
|
||||||
|
if (map.containsKey(groupId)) {
|
||||||
|
return map[groupId];
|
||||||
|
} else {
|
||||||
|
return GroupEntity(id: groupId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
BuiltMap<String, GroupEntity> get map;
|
BuiltMap<String, GroupEntity> get map;
|
||||||
BuiltList<String> get list;
|
BuiltList<String> get list;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -72,15 +72,21 @@ class CreditEditDetailsVM extends EntityEditDetailsVM {
|
||||||
factory CreditEditDetailsVM.fromStore(Store<AppState> store) {
|
factory CreditEditDetailsVM.fromStore(Store<AppState> store) {
|
||||||
final AppState state = store.state;
|
final AppState state = store.state;
|
||||||
final credit = state.creditUIState.editing;
|
final credit = state.creditUIState.editing;
|
||||||
|
final company = state.company;
|
||||||
|
|
||||||
return CreditEditDetailsVM(
|
return CreditEditDetailsVM(
|
||||||
state: state,
|
state: state,
|
||||||
company: state.company,
|
company: company,
|
||||||
invoice: credit,
|
invoice: credit,
|
||||||
onChanged: (InvoiceEntity credit) => store.dispatch(UpdateCredit(credit)),
|
onChanged: (InvoiceEntity credit) => store.dispatch(UpdateCredit(credit)),
|
||||||
clientMap: state.clientState.map,
|
clientMap: state.clientState.map,
|
||||||
clientList: state.clientState.list,
|
clientList: state.clientState.list,
|
||||||
onClientChanged: (invoice, client) {
|
onClientChanged: (invoice, client) {
|
||||||
|
if (company.convertProductExchangeRate && client != null) {
|
||||||
|
store.dispatch(UpdateCredit(credit.rebuild((b) => b
|
||||||
|
..exchangeRate = state
|
||||||
|
.staticState.currencyMap[client.currencyId].exchangeRate)));
|
||||||
|
}
|
||||||
store.dispatch(UpdateCreditClient(client: client));
|
store.dispatch(UpdateCreditClient(client: client));
|
||||||
},
|
},
|
||||||
onAddClientPressed: (context, completer) {
|
onAddClientPressed: (context, completer) {
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:invoiceninja_flutter/constants.dart';
|
import 'package:invoiceninja_flutter/constants.dart';
|
||||||
|
import 'package:invoiceninja_flutter/data/models/client_model.dart';
|
||||||
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
|
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/forms/app_tab_bar.dart';
|
import 'package:invoiceninja_flutter/ui/app/forms/app_tab_bar.dart';
|
||||||
|
|
@ -62,7 +63,6 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
final _privateNotesController = TextEditingController();
|
final _privateNotesController = TextEditingController();
|
||||||
final _termsController = TextEditingController();
|
final _termsController = TextEditingController();
|
||||||
final _footerController = TextEditingController();
|
final _footerController = TextEditingController();
|
||||||
final _exchangeRateController = TextEditingController();
|
|
||||||
|
|
||||||
List<TextEditingController> _controllers = [];
|
List<TextEditingController> _controllers = [];
|
||||||
final _debouncer = Debouncer();
|
final _debouncer = Debouncer();
|
||||||
|
|
@ -94,7 +94,6 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
_privateNotesController,
|
_privateNotesController,
|
||||||
_termsController,
|
_termsController,
|
||||||
_footerController,
|
_footerController,
|
||||||
_exchangeRateController,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
_controllers
|
_controllers
|
||||||
|
|
@ -123,8 +122,6 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
_privateNotesController.text = invoice.privateNotes;
|
_privateNotesController.text = invoice.privateNotes;
|
||||||
_termsController.text = invoice.terms;
|
_termsController.text = invoice.terms;
|
||||||
_footerController.text = invoice.footer;
|
_footerController.text = invoice.footer;
|
||||||
_exchangeRateController.text = formatNumber(invoice.exchangeRate, context,
|
|
||||||
formatNumberType: FormatNumberType.input);
|
|
||||||
|
|
||||||
_controllers
|
_controllers
|
||||||
.forEach((dynamic controller) => controller.addListener(_onChanged));
|
.forEach((dynamic controller) => controller.addListener(_onChanged));
|
||||||
|
|
@ -162,8 +159,7 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
..publicNotes = _publicNotesController.text.trim()
|
..publicNotes = _publicNotesController.text.trim()
|
||||||
..privateNotes = _privateNotesController.text.trim()
|
..privateNotes = _privateNotesController.text.trim()
|
||||||
..terms = _termsController.text.trim()
|
..terms = _termsController.text.trim()
|
||||||
..footer = _footerController.text.trim()
|
..footer = _footerController.text.trim());
|
||||||
..exchangeRate = parseDouble(_exchangeRateController.text));
|
|
||||||
if (invoice != widget.viewModel.invoice) {
|
if (invoice != widget.viewModel.invoice) {
|
||||||
widget.viewModel.onChanged(invoice);
|
widget.viewModel.onChanged(invoice);
|
||||||
}
|
}
|
||||||
|
|
@ -201,8 +197,20 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
autofocus: kIsWeb,
|
autofocus: kIsWeb,
|
||||||
clientId: invoice.clientId,
|
clientId: invoice.clientId,
|
||||||
clientState: viewModel.state.clientState,
|
clientState: viewModel.state.clientState,
|
||||||
onSelected: (client) =>
|
onSelected: (client) {
|
||||||
viewModel.onClientChanged(invoice, client),
|
viewModel.onClientChanged(invoice, client);
|
||||||
|
/*
|
||||||
|
final currencyId = (client as ClientEntity)?.currencyId;
|
||||||
|
if (company.convertProductExchangeRate &&
|
||||||
|
client != null) {
|
||||||
|
_exchangeRateController.text = formatNumber(
|
||||||
|
viewModel.state.staticState
|
||||||
|
.currencyMap[currencyId].exchangeRate,
|
||||||
|
context,
|
||||||
|
formatNumberType: FormatNumberType.input);
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
},
|
||||||
onAddPressed: (completer) =>
|
onAddPressed: (completer) =>
|
||||||
viewModel.onAddClientPressed(context, completer),
|
viewModel.onAddClientPressed(context, completer),
|
||||||
)
|
)
|
||||||
|
|
@ -441,8 +449,16 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: DecoratedFormField(
|
child: DecoratedFormField(
|
||||||
|
key: ValueKey('__rate_${invoice.clientId}__'),
|
||||||
label: localization.exchangeRate,
|
label: localization.exchangeRate,
|
||||||
controller: _exchangeRateController,
|
initialValue: formatNumber(
|
||||||
|
invoice.exchangeRate, context,
|
||||||
|
formatNumberType:
|
||||||
|
FormatNumberType.input),
|
||||||
|
onChanged: (value) => viewModel.onChanged(
|
||||||
|
invoice.rebuild((b) => b
|
||||||
|
..exchangeRate =
|
||||||
|
parseDouble(value))),
|
||||||
keyboardType:
|
keyboardType:
|
||||||
TextInputType.numberWithOptions(
|
TextInputType.numberWithOptions(
|
||||||
decimal: true),
|
decimal: true),
|
||||||
|
|
@ -534,6 +550,7 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: localization.total,
|
labelText: localization.total,
|
||||||
),
|
),
|
||||||
|
textAlign: TextAlign.end,
|
||||||
key: ValueKey(
|
key: ValueKey(
|
||||||
'__invoice_total_${invoiceTotal}_${invoice.clientId}__'),
|
'__invoice_total_${invoiceTotal}_${invoice.clientId}__'),
|
||||||
initialValue: formatNumber(invoiceTotal, context,
|
initialValue: formatNumber(invoiceTotal, context,
|
||||||
|
|
|
||||||
|
|
@ -91,16 +91,23 @@ class InvoiceEditDetailsVM extends EntityEditDetailsVM {
|
||||||
factory InvoiceEditDetailsVM.fromStore(Store<AppState> store) {
|
factory InvoiceEditDetailsVM.fromStore(Store<AppState> store) {
|
||||||
final AppState state = store.state;
|
final AppState state = store.state;
|
||||||
final invoice = state.invoiceUIState.editing;
|
final invoice = state.invoiceUIState.editing;
|
||||||
|
final company = state.company;
|
||||||
|
|
||||||
return InvoiceEditDetailsVM(
|
return InvoiceEditDetailsVM(
|
||||||
state: state,
|
state: state,
|
||||||
company: state.company,
|
company: company,
|
||||||
invoice: invoice,
|
invoice: invoice,
|
||||||
onChanged: (InvoiceEntity invoice) =>
|
onChanged: (InvoiceEntity invoice) =>
|
||||||
store.dispatch(UpdateInvoice(invoice)),
|
store.dispatch(UpdateInvoice(invoice)),
|
||||||
clientMap: state.clientState.map,
|
clientMap: state.clientState.map,
|
||||||
clientList: state.clientState.list,
|
clientList: state.clientState.list,
|
||||||
onClientChanged: (invoice, client) {
|
onClientChanged: (invoice, client) {
|
||||||
|
print('## onClientChanged: ${client?.id}');
|
||||||
|
if (company.convertProductExchangeRate && client != null) {
|
||||||
|
store.dispatch(UpdateInvoice(invoice.rebuild((b) => b
|
||||||
|
..exchangeRate = state
|
||||||
|
.staticState.currencyMap[client.currencyId].exchangeRate)));
|
||||||
|
}
|
||||||
store.dispatch(UpdateInvoiceClient(client: client));
|
store.dispatch(UpdateInvoiceClient(client: client));
|
||||||
},
|
},
|
||||||
onAddClientPressed: (context, completer) {
|
onAddClientPressed: (context, completer) {
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,9 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
|
||||||
final item = lineItems[index];
|
final item = lineItems[index];
|
||||||
final product = productState.map[suggestion];
|
final product = productState.map[suggestion];
|
||||||
final client = state.clientState.get(invoice.clientId);
|
final client = state.clientState.get(invoice.clientId);
|
||||||
|
final currencyId = client.getCurrencyId(
|
||||||
|
company: company,
|
||||||
|
group: state.groupState.get(client.groupId));
|
||||||
|
|
||||||
double cost = product.price;
|
double cost = product.price;
|
||||||
if (company.convertProductExchangeRate &&
|
if (company.convertProductExchangeRate &&
|
||||||
|
|
@ -239,7 +242,7 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
|
||||||
toCurrencyId: client.currencyId);
|
toCurrencyId: client.currencyId);
|
||||||
cost = round(
|
cost = round(
|
||||||
cost,
|
cost,
|
||||||
state.staticState.currencyMap[client.currencyId]
|
state.staticState.currencyMap[currencyId]
|
||||||
.precision);
|
.precision);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -428,7 +431,7 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
|
||||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
padding: const EdgeInsets.only(right: kTableColumnGap),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
key: ValueKey(
|
key: ValueKey(
|
||||||
'__total_${index}_${lineItems[index].total}__'),
|
'__total_${index}_${lineItems[index].total}_${invoice.clientId}__'),
|
||||||
readOnly: true,
|
readOnly: true,
|
||||||
enabled: false,
|
enabled: false,
|
||||||
initialValue: formatNumber(
|
initialValue: formatNumber(
|
||||||
|
|
|
||||||
|
|
@ -71,15 +71,21 @@ class QuoteEditDetailsVM extends EntityEditDetailsVM {
|
||||||
factory QuoteEditDetailsVM.fromStore(Store<AppState> store) {
|
factory QuoteEditDetailsVM.fromStore(Store<AppState> store) {
|
||||||
final AppState state = store.state;
|
final AppState state = store.state;
|
||||||
final quote = state.quoteUIState.editing;
|
final quote = state.quoteUIState.editing;
|
||||||
|
final company = state.company;
|
||||||
|
|
||||||
return QuoteEditDetailsVM(
|
return QuoteEditDetailsVM(
|
||||||
state: state,
|
state: state,
|
||||||
company: state.company,
|
company: company,
|
||||||
invoice: quote,
|
invoice: quote,
|
||||||
onChanged: (InvoiceEntity quote) => store.dispatch(UpdateQuote(quote)),
|
onChanged: (InvoiceEntity quote) => store.dispatch(UpdateQuote(quote)),
|
||||||
clientMap: state.clientState.map,
|
clientMap: state.clientState.map,
|
||||||
clientList: state.clientState.list,
|
clientList: state.clientState.list,
|
||||||
onClientChanged: (invoice, client) {
|
onClientChanged: (quote, client) {
|
||||||
|
if (company.convertProductExchangeRate && client != null) {
|
||||||
|
store.dispatch(UpdateQuote(quote.rebuild((b) => b
|
||||||
|
..exchangeRate = state
|
||||||
|
.staticState.currencyMap[client.currencyId].exchangeRate)));
|
||||||
|
}
|
||||||
store.dispatch(UpdateQuoteClient(client: client));
|
store.dispatch(UpdateQuoteClient(client: client));
|
||||||
},
|
},
|
||||||
onAddClientPressed: (context, completer) {
|
onAddClientPressed: (context, completer) {
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,8 @@ String formatNumber(
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print('## Formatting... CLIENT: ${client?.name}, CURRENCY: ${currency.name}');
|
||||||
|
|
||||||
if (formatNumberType == FormatNumberType.money) {
|
if (formatNumberType == FormatNumberType.money) {
|
||||||
value = round(value, currency.precision);
|
value = round(value, currency.precision);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,9 @@ double getExchangeRate(BuildContext context,
|
||||||
|
|
||||||
double getExchangeRateWithMap(BuiltMap<String, CurrencyEntity> currencyMap,
|
double getExchangeRateWithMap(BuiltMap<String, CurrencyEntity> currencyMap,
|
||||||
{String fromCurrencyId, String toCurrencyId}) {
|
{String fromCurrencyId, String toCurrencyId}) {
|
||||||
|
if (fromCurrencyId == null || toCurrencyId == null) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
final fromCurrency = currencyMap[fromCurrencyId];
|
final fromCurrency = currencyMap[fromCurrencyId];
|
||||||
final toCurrency = currencyMap[toCurrencyId];
|
final toCurrency = currencyMap[toCurrencyId];
|
||||||
// TODO replace with data from server
|
// TODO replace with data from server
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue