Support group/client default rates
This commit is contained in:
parent
9a417c41be
commit
549a546b9e
|
|
@ -9,6 +9,7 @@ import 'package:invoiceninja_flutter/data/models/models.dart';
|
|||
import 'package:invoiceninja_flutter/data/models/quote_model.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/recurring_invoice_model.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||
import 'package:invoiceninja_flutter/utils/money.dart';
|
||||
import 'package:invoiceninja_flutter/utils/strings.dart';
|
||||
|
|
@ -119,6 +120,8 @@ abstract class InvoiceEntity extends Object
|
|||
EntityType entityType,
|
||||
}) {
|
||||
final company = state?.company;
|
||||
final settings = getClientSettings(state, client);
|
||||
|
||||
double exchangeRate = 1;
|
||||
if ((client?.currencyId ?? '').isNotEmpty) {
|
||||
exchangeRate = getExchangeRate(
|
||||
|
|
@ -148,22 +151,22 @@ abstract class InvoiceEntity extends Object
|
|||
footer: '',
|
||||
designId: '',
|
||||
taxName1: (company?.numberOfInvoiceTaxRates ?? 0) >= 1
|
||||
? company?.settings?.defaultTaxName1 ?? ''
|
||||
? settings.defaultTaxName1 ?? ''
|
||||
: '',
|
||||
taxRate1: (company?.numberOfInvoiceTaxRates ?? 0) >= 1
|
||||
? company?.settings?.defaultTaxRate1 ?? 0.0
|
||||
? settings.defaultTaxRate1 ?? 0.0
|
||||
: 0,
|
||||
taxName2: (company?.numberOfInvoiceTaxRates ?? 0) >= 2
|
||||
? company?.settings?.defaultTaxName2 ?? ''
|
||||
? settings.defaultTaxName2 ?? ''
|
||||
: '',
|
||||
taxRate2: (company?.numberOfInvoiceTaxRates ?? 0) >= 2
|
||||
? company?.settings?.defaultTaxRate2 ?? 0.0
|
||||
? settings.defaultTaxRate2 ?? 0.0
|
||||
: 0,
|
||||
taxName3: (company?.numberOfInvoiceTaxRates ?? 0) >= 3
|
||||
? company?.settings?.defaultTaxName3 ?? ''
|
||||
? settings.defaultTaxName3 ?? ''
|
||||
: '',
|
||||
taxRate3: (company?.numberOfInvoiceTaxRates ?? 0) >= 3
|
||||
? company?.settings?.defaultTaxRate3 ?? 0.0
|
||||
? settings.defaultTaxRate3 ?? 0.0
|
||||
: 0,
|
||||
isAmountDiscount: false,
|
||||
partial: 0.0,
|
||||
|
|
|
|||
|
|
@ -59,6 +59,24 @@ abstract class SettingsEntity
|
|||
defaultValidUntil: clientSettings?.defaultValidUntil ??
|
||||
groupSettings?.defaultValidUntil ??
|
||||
companySettings?.defaultValidUntil,
|
||||
defaultTaxRate1: clientSettings?.defaultTaxRate1 ??
|
||||
groupSettings?.defaultTaxRate1 ??
|
||||
companySettings?.defaultTaxRate1,
|
||||
defaultTaxName1: clientSettings?.defaultTaxName1 ??
|
||||
groupSettings?.defaultTaxName1 ??
|
||||
companySettings?.defaultTaxName1,
|
||||
defaultTaxRate2: clientSettings?.defaultTaxRate2 ??
|
||||
groupSettings?.defaultTaxRate2 ??
|
||||
companySettings?.defaultTaxRate2,
|
||||
defaultTaxName2: clientSettings?.defaultTaxName2 ??
|
||||
groupSettings?.defaultTaxName2 ??
|
||||
companySettings?.defaultTaxName2,
|
||||
defaultTaxRate3: clientSettings?.defaultTaxRate3 ??
|
||||
groupSettings?.defaultTaxRate3 ??
|
||||
companySettings?.defaultTaxRate3,
|
||||
defaultTaxName3: clientSettings?.defaultTaxName3 ??
|
||||
groupSettings?.defaultTaxName3 ??
|
||||
companySettings?.defaultTaxName3,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import 'package:invoiceninja_flutter/data/models/static/static_data_model.dart';
|
|||
import 'package:invoiceninja_flutter/main_app.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/credit/credit_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/dashboard/dashboard_actions.dart';
|
||||
|
|
@ -1110,11 +1111,7 @@ void editEntity({
|
|||
case EntityType.invoice:
|
||||
final invoice = entity as InvoiceEntity;
|
||||
final client = state.clientState.get(invoice.clientId);
|
||||
final settings = SettingsEntity(
|
||||
clientSettings: client.settings,
|
||||
groupSettings: state.groupState.get(client.groupId).settings,
|
||||
companySettings: state.company.settings,
|
||||
);
|
||||
final settings = getClientSettings(state, client);
|
||||
|
||||
if (settings.lockInvoices == SettingsEntity.LOCK_INVOICES_PAID &&
|
||||
invoice.isPaid) {
|
||||
|
|
|
|||
|
|
@ -127,6 +127,22 @@ List<String> filteredClientsSelector(
|
|||
return list;
|
||||
}
|
||||
|
||||
SettingsEntity getClientSettings(AppState state, ClientEntity client) {
|
||||
if (state == null) {
|
||||
return SettingsEntity();
|
||||
}
|
||||
|
||||
client ??= ClientEntity();
|
||||
final company = state.company;
|
||||
final group = state.groupState.get(client.groupId);
|
||||
|
||||
return SettingsEntity(
|
||||
clientSettings: client.settings,
|
||||
groupSettings: group.settings,
|
||||
companySettings: company.settings,
|
||||
);
|
||||
}
|
||||
|
||||
bool hasClientChanges(
|
||||
ClientEntity client, BuiltMap<String, ClientEntity> clientMap) =>
|
||||
client.isNew ? client.isChanged : client != clientMap[client.id];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:invoiceninja_flutter/data/models/design_model.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_selectors.dart';
|
||||
import 'package:memoize/memoize.dart';
|
||||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
|
|
@ -68,12 +69,7 @@ bool hasDesignChanges(
|
|||
String getDesignIdForClientByEntity(
|
||||
{AppState state, String clientId, EntityType entityType}) {
|
||||
final client = state.clientState.get(clientId);
|
||||
final group = state.groupState.get(client.groupId);
|
||||
final settings = SettingsEntity(
|
||||
clientSettings: client.settings,
|
||||
groupSettings: group.settings,
|
||||
companySettings: state.company.settings,
|
||||
);
|
||||
final settings = getClientSettings(state, client);
|
||||
switch (entityType) {
|
||||
case EntityType.invoice:
|
||||
return settings.defaultInvoiceDesignId;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import 'package:invoiceninja_flutter/constants.dart';
|
|||
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_selectors.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/decorated_form_field.dart';
|
||||
|
|
@ -159,11 +160,7 @@ class _InvoiceEmailViewState extends State<InvoiceEmailView>
|
|||
final invoice = widget.viewModel.invoice;
|
||||
final client = viewModel.client;
|
||||
final state = viewModel.state;
|
||||
final settings = SettingsEntity(
|
||||
clientSettings: client.settings,
|
||||
groupSettings: state.groupState.get(client.groupId).settings,
|
||||
companySettings: state.company.settings,
|
||||
);
|
||||
final settings = getClientSettings(state, client);
|
||||
final contacts = invoice.invitations
|
||||
.map((invitation) => client.contacts.firstWhere(
|
||||
(contact) => contact.id == invitation.contactId,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import 'package:flutter_redux/flutter_redux.dart';
|
|||
import 'package:invoiceninja_flutter/data/models/serializers.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/app_scrollbar.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/buttons/elevated_button.dart';
|
||||
import 'package:material_design_icons_flutter/material_design_icons_flutter.dart';
|
||||
|
|
@ -212,12 +213,7 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
|||
!item.isEmpty && item.typeId == InvoiceItemEntity.TYPE_TASK)
|
||||
.length;
|
||||
|
||||
final settings = SettingsEntity(
|
||||
clientSettings: client.settings,
|
||||
groupSettings: state.groupState.get(client.groupId).settings,
|
||||
companySettings: state.company.settings,
|
||||
);
|
||||
|
||||
final settings = getClientSettings(state, client);
|
||||
final terms = entityType == EntityType.quote
|
||||
? settings.defaultValidUntil
|
||||
: settings.defaultPaymentTerms;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:invoiceninja_flutter/constants.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/forms/app_dropdown_button.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/forms/client_picker.dart';
|
||||
|
|
@ -137,11 +138,7 @@ class InvoiceEditDetailsState extends State<InvoiceEditDetails> {
|
|||
state.getEntity(invoice.entityType, invoice.id) as InvoiceEntity;
|
||||
|
||||
final client = state.clientState.get(invoice.clientId);
|
||||
final settings = SettingsEntity(
|
||||
clientSettings: client.settings,
|
||||
groupSettings: state.groupState.get(client.groupId).settings,
|
||||
companySettings: state.company.settings,
|
||||
);
|
||||
final settings = getClientSettings(state, client);
|
||||
final terms = widget.entityType == EntityType.quote
|
||||
? settings.defaultValidUntil
|
||||
: settings.defaultPaymentTerms;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_selectors.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/scrollable_listview.dart';
|
||||
|
|
@ -83,12 +84,7 @@ class InvoiceEditNotesState extends State<InvoiceEditNotes> {
|
|||
final state = viewModel.state;
|
||||
final invoice = viewModel.invoice;
|
||||
final client = state.clientState.get(invoice.clientId);
|
||||
|
||||
final settings = SettingsEntity(
|
||||
clientSettings: client.settings,
|
||||
groupSettings: state.groupState.get(client.groupId).settings,
|
||||
companySettings: state.company.settings,
|
||||
);
|
||||
final settings = getClientSettings(state, client);
|
||||
|
||||
return ScrollableListView(
|
||||
children: <Widget>[
|
||||
|
|
|
|||
|
|
@ -120,11 +120,10 @@ class _SettingsListState extends State<SettingsList> {
|
|||
section: kSettingsOnlinePayments,
|
||||
viewModel: widget.viewModel,
|
||||
),
|
||||
if (showAll)
|
||||
SettingsListTile(
|
||||
section: kSettingsTaxSettings,
|
||||
viewModel: widget.viewModel,
|
||||
),
|
||||
SettingsListTile(
|
||||
section: kSettingsTaxSettings,
|
||||
viewModel: widget.viewModel,
|
||||
),
|
||||
if (showAll)
|
||||
SettingsListTile(
|
||||
section: kSettingsProducts,
|
||||
|
|
|
|||
|
|
@ -56,76 +56,74 @@ class _TaxSettingsState extends State<TaxSettings> {
|
|||
formKey: _formKey,
|
||||
focusNode: _focusNode,
|
||||
children: <Widget>[
|
||||
FormCard(
|
||||
children: <Widget>[
|
||||
NumberOfRatesSelector(
|
||||
label: localization.invoiceTaxRates,
|
||||
numberOfRates: company.numberOfInvoiceTaxRates,
|
||||
onChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..numberOfInvoiceTaxRates = value)),
|
||||
),
|
||||
NumberOfRatesSelector(
|
||||
label: localization.itemTaxRates,
|
||||
numberOfRates: company.numberOfItemTaxRates,
|
||||
onChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..numberOfItemTaxRates = value)),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
BoolDropdownButton(
|
||||
iconData: MdiIcons.percent,
|
||||
label: localization.inclusiveTaxes,
|
||||
value: settings.enableInclusiveTaxes,
|
||||
onChanged: (value) => viewModel.onSettingsChanged(
|
||||
settings.rebuild((b) => b..enableInclusiveTaxes = value)),
|
||||
helpLabel:
|
||||
'\n${localization.exclusive}: 100 + 10% = 100 + 10\n${localization.inclusive}: 100 + 10% = 90.91 + 9.09',
|
||||
),
|
||||
],
|
||||
),
|
||||
if (company.enableFirstInvoiceTaxRate &&
|
||||
state.taxRateState.list.isNotEmpty)
|
||||
if (!state.settingsUIState.isFiltered)
|
||||
FormCard(
|
||||
children: <Widget>[
|
||||
NumberOfRatesSelector(
|
||||
label: localization.invoiceTaxRates,
|
||||
numberOfRates: company.numberOfInvoiceTaxRates,
|
||||
onChanged: (value) => viewModel.onCompanyChanged(company
|
||||
.rebuild((b) => b..numberOfInvoiceTaxRates = value)),
|
||||
),
|
||||
NumberOfRatesSelector(
|
||||
label: localization.itemTaxRates,
|
||||
numberOfRates: company.numberOfItemTaxRates,
|
||||
onChanged: (value) => viewModel.onCompanyChanged(
|
||||
company.rebuild((b) => b..numberOfItemTaxRates = value)),
|
||||
),
|
||||
SizedBox(height: 16),
|
||||
BoolDropdownButton(
|
||||
iconData: MdiIcons.percent,
|
||||
label: localization.inclusiveTaxes,
|
||||
value: settings.enableInclusiveTaxes,
|
||||
onChanged: (value) => viewModel.onSettingsChanged(
|
||||
settings.rebuild((b) => b..enableInclusiveTaxes = value)),
|
||||
helpLabel:
|
||||
'\n${localization.exclusive}: 100 + 10% = 100 + 10\n${localization.inclusive}: 100 + 10% = 90.91 + 9.09',
|
||||
),
|
||||
],
|
||||
),
|
||||
FormCard(
|
||||
children: <Widget>[
|
||||
TaxRateDropdown(
|
||||
onSelected: (taxRate) =>
|
||||
viewModel.onSettingsChanged(settings.rebuild((b) => b
|
||||
..defaultTaxName1 = taxRate.name
|
||||
..defaultTaxRate1 = taxRate.rate)),
|
||||
labelText: localization.defaultTaxRate,
|
||||
initialTaxName: settings.defaultTaxName1,
|
||||
initialTaxRate: settings.defaultTaxRate1,
|
||||
),
|
||||
if (company.enableSecondInvoiceTaxRate)
|
||||
TaxRateDropdown(
|
||||
onSelected: (taxRate) =>
|
||||
viewModel.onSettingsChanged(settings.rebuild((b) => b
|
||||
..defaultTaxName1 = taxRate.name
|
||||
..defaultTaxRate1 = taxRate.rate)),
|
||||
..defaultTaxName2 = taxRate.name
|
||||
..defaultTaxRate2 = taxRate.rate)),
|
||||
labelText: localization.defaultTaxRate,
|
||||
initialTaxName: settings.defaultTaxName1,
|
||||
initialTaxRate: settings.defaultTaxRate1,
|
||||
initialTaxName: settings.defaultTaxName2,
|
||||
initialTaxRate: settings.defaultTaxRate2,
|
||||
),
|
||||
if (company.enableSecondInvoiceTaxRate)
|
||||
TaxRateDropdown(
|
||||
onSelected: (taxRate) =>
|
||||
viewModel.onSettingsChanged(settings.rebuild((b) => b
|
||||
..defaultTaxName2 = taxRate.name
|
||||
..defaultTaxRate2 = taxRate.rate)),
|
||||
labelText: localization.defaultTaxRate,
|
||||
initialTaxName: settings.defaultTaxName2,
|
||||
initialTaxRate: settings.defaultTaxRate2,
|
||||
),
|
||||
if (company.enableThirdInvoiceTaxRate)
|
||||
TaxRateDropdown(
|
||||
onSelected: (taxRate) =>
|
||||
viewModel.onSettingsChanged(settings.rebuild((b) => b
|
||||
..defaultTaxName3 = taxRate.name
|
||||
..defaultTaxRate3 = taxRate.rate)),
|
||||
labelText: localization.defaultTaxRate,
|
||||
initialTaxName: settings.defaultTaxName3,
|
||||
initialTaxRate: settings.defaultTaxRate3,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (!state.uiState.settingsUIState.isFiltered)
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: AppButton(
|
||||
iconData: Icons.settings,
|
||||
label: localization.configureRates.toUpperCase(),
|
||||
onPressed: () => viewModel.onConfigureRatesPressed(context),
|
||||
),
|
||||
if (company.enableThirdInvoiceTaxRate)
|
||||
TaxRateDropdown(
|
||||
onSelected: (taxRate) =>
|
||||
viewModel.onSettingsChanged(settings.rebuild((b) => b
|
||||
..defaultTaxName3 = taxRate.name
|
||||
..defaultTaxRate3 = taxRate.rate)),
|
||||
labelText: localization.defaultTaxRate,
|
||||
initialTaxName: settings.defaultTaxName3,
|
||||
initialTaxRate: settings.defaultTaxRate3,
|
||||
),
|
||||
],
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16.0),
|
||||
child: AppButton(
|
||||
iconData: Icons.settings,
|
||||
label: localization.configureRates.toUpperCase(),
|
||||
onPressed: () => viewModel.onConfigureRatesPressed(context),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -2,9 +2,14 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/constants.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/client_model.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/company_model.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/group_model.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/settings_model.dart';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/company/company_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/group/group_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/settings/settings_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/settings/tax_settings.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
|
|
@ -56,10 +61,27 @@ class TaxSettingsVM {
|
|||
onSavePressed: (context) {
|
||||
Debouncer.runOnComplete(() {
|
||||
final settingsUIState = store.state.uiState.settingsUIState;
|
||||
final completer = snackBarCompleter<Null>(
|
||||
context, AppLocalization.of(context).savedSettings);
|
||||
store.dispatch(SaveCompanyRequest(
|
||||
completer: completer, company: settingsUIState.company));
|
||||
|
||||
switch (settingsUIState.entityType) {
|
||||
case EntityType.company:
|
||||
final completer = snackBarCompleter<Null>(
|
||||
context, AppLocalization.of(context).savedSettings);
|
||||
store.dispatch(SaveCompanyRequest(
|
||||
completer: completer, company: settingsUIState.company));
|
||||
break;
|
||||
case EntityType.group:
|
||||
final completer = snackBarCompleter<GroupEntity>(
|
||||
context, AppLocalization.of(context).savedSettings);
|
||||
store.dispatch(SaveGroupRequest(
|
||||
completer: completer, group: settingsUIState.group));
|
||||
break;
|
||||
case EntityType.client:
|
||||
final completer = snackBarCompleter<ClientEntity>(
|
||||
context, AppLocalization.of(context).savedSettings);
|
||||
store.dispatch(SaveClientRequest(
|
||||
completer: completer, client: settingsUIState.client));
|
||||
break;
|
||||
}
|
||||
});
|
||||
},
|
||||
onConfigureRatesPressed: (context) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue