Track inventory

This commit is contained in:
Hillel Coren 2022-06-03 15:49:50 +03:00
parent e07910c69b
commit 15f4f11d3a
3 changed files with 88 additions and 32 deletions

View File

@ -98,17 +98,6 @@ class _CompanyDetailsState extends State<CompanyDetails>
store.dispatch(UpdateSettingsTab(tabIndex: _controller.index)); store.dispatch(UpdateSettingsTab(tabIndex: _controller.index));
} }
@override
void dispose() {
_controller.removeListener(_onTabChanged);
_controller.dispose();
_controllers.forEach((dynamic controller) {
controller.removeListener(_onSettingsChanged);
controller.dispose();
});
super.dispose();
}
@override @override
void didChangeDependencies() { void didChangeDependencies() {
_controllers = [ _controllers = [
@ -169,6 +158,18 @@ class _CompanyDetailsState extends State<CompanyDetails>
super.didChangeDependencies(); super.didChangeDependencies();
} }
@override
void dispose() {
_focusNode.dispose();
_controller.removeListener(_onTabChanged);
_controller.dispose();
_controllers.forEach((dynamic controller) {
controller.removeListener(_onSettingsChanged);
controller.dispose();
});
super.dispose();
}
void _onSettingsChanged() { void _onSettingsChanged() {
final settings = widget.viewModel.settings.rebuild((b) => b final settings = widget.viewModel.settings.rebuild((b) => b
..name = _nameController.text.trim() ..name = _nameController.text.trim()

View File

@ -6,7 +6,10 @@ import 'package:invoiceninja_flutter/ui/app/edit_scaffold.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_form.dart'; import 'package:invoiceninja_flutter/ui/app/forms/app_form.dart';
import 'package:invoiceninja_flutter/ui/app/forms/bool_dropdown_button.dart'; import 'package:invoiceninja_flutter/ui/app/forms/bool_dropdown_button.dart';
import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart';
import 'package:invoiceninja_flutter/ui/settings/product_settings_vm.dart'; import 'package:invoiceninja_flutter/ui/settings/product_settings_vm.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/formatting.dart';
import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/utils/localization.dart';
class ProductSettings extends StatefulWidget { class ProductSettings extends StatefulWidget {
@ -25,6 +28,9 @@ class _ProductSettingsState extends State<ProductSettings> {
static final GlobalKey<FormState> _formKey = static final GlobalKey<FormState> _formKey =
GlobalKey<FormState>(debugLabel: '_productSettings'); GlobalKey<FormState>(debugLabel: '_productSettings');
FocusScopeNode _focusNode; FocusScopeNode _focusNode;
final _debouncer = Debouncer(sendFirstAction: true);
final _stockThresholdController = TextEditingController();
List<TextEditingController> _controllers = [];
@override @override
void initState() { void initState() {
@ -32,12 +38,49 @@ class _ProductSettingsState extends State<ProductSettings> {
_focusNode = FocusScopeNode(); _focusNode = FocusScopeNode();
} }
@override
void didChangeDependencies() {
_controllers = [_stockThresholdController];
_controllers
.forEach((dynamic controller) => controller.removeListener(_onChanged));
final viewModel = widget.viewModel;
final company = viewModel.state.company;
_stockThresholdController.text = formatNumber(
company.stockNotificationThreshold.toDouble(),
context,
formatNumberType: FormatNumberType.int,
);
_controllers
.forEach((dynamic controller) => controller.addListener(_onChanged));
super.didChangeDependencies();
}
@override @override
void dispose() { void dispose() {
_focusNode.dispose(); _focusNode.dispose();
_controllers.forEach((dynamic controller) {
controller.removeListener(_onChanged);
controller.dispose();
});
super.dispose(); super.dispose();
} }
void _onChanged() {
final company = widget.viewModel.company.rebuild((b) => b
..stockNotificationThreshold =
parseInt(_stockThresholdController.text.trim()));
if (company != widget.viewModel.company) {
_debouncer.run(() {
widget.viewModel.onCompanyChanged(company);
});
}
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final localization = AppLocalization.of(context); final localization = AppLocalization.of(context);
@ -51,6 +94,32 @@ class _ProductSettingsState extends State<ProductSettings> {
formKey: _formKey, formKey: _formKey,
focusNode: _focusNode, focusNode: _focusNode,
children: <Widget>[ children: <Widget>[
FormCard(
children: [
SwitchListTile(
activeColor: Theme.of(context).colorScheme.secondary,
title: Text(localization.trackInventory),
value: company.trackInventory,
subtitle: Text(localization.trackInventoryHelp),
onChanged: (value) => viewModel.onCompanyChanged(
company.rebuild((b) => b..trackInventory = value)),
),
SwitchListTile(
activeColor: Theme.of(context).colorScheme.secondary,
title: Text(localization.stockNotifications),
value: company.stockNotification,
subtitle: Text(localization.stockNotificationsHelp),
onChanged: (value) => viewModel.onCompanyChanged(
company.rebuild((b) => b..stockNotification = value)),
),
if (company.trackInventory && company.stockNotification)
DecoratedFormField(
keyboardType: TextInputType.number,
controller: _stockThresholdController,
label: localization.notificationThreshold,
),
],
),
FormCard( FormCard(
children: <Widget>[ children: <Widget>[
SwitchListTile( SwitchListTile(
@ -88,6 +157,7 @@ class _ProductSettingsState extends State<ProductSettings> {
], ],
), ),
FormCard( FormCard(
isLast: true,
children: <Widget>[ children: <Widget>[
SwitchListTile( SwitchListTile(
activeColor: Theme.of(context).colorScheme.secondary, activeColor: Theme.of(context).colorScheme.secondary,
@ -135,27 +205,6 @@ class _ProductSettingsState extends State<ProductSettings> {
) )
], ],
), ),
FormCard(
isLast: true,
children: [
SwitchListTile(
activeColor: Theme.of(context).colorScheme.secondary,
title: Text(localization.trackInventory),
value: company.trackInventory,
subtitle: Text(localization.trackInventoryHelp),
onChanged: (value) => viewModel.onCompanyChanged(
company.rebuild((b) => b..trackInventory = value)),
),
SwitchListTile(
activeColor: Theme.of(context).colorScheme.secondary,
title: Text(localization.stockNotifications),
value: company.stockNotification,
subtitle: Text(localization.stockNotificationsHelp),
onChanged: (value) => viewModel.onCompanyChanged(
company.rebuild((b) => b..stockNotification = value)),
),
],
),
], ],
), ),
); );

View File

@ -16,6 +16,7 @@ mixin LocalizationsProvider on LocaleCodeAware {
static final Map<String, Map<String, String>> _localizedValues = { static final Map<String, Map<String, String>> _localizedValues = {
'en': { 'en': {
// STARTER: lang key - do not remove comment // STARTER: lang key - do not remove comment
'notification_threshold': 'Notification Threshold',
'track_inventory': 'Track Inventory', 'track_inventory': 'Track Inventory',
'track_inventory_help': 'track_inventory_help':
'Display a product stock field and update when invoices are sent', 'Display a product stock field and update when invoices are sent',
@ -70568,6 +70569,11 @@ mixin LocalizationsProvider on LocaleCodeAware {
_localizedValues[localeCode]['stock_notifications_help'] ?? _localizedValues[localeCode]['stock_notifications_help'] ??
_localizedValues['en']['stock_notifications_help']; _localizedValues['en']['stock_notifications_help'];
String get notificationThreshold =>
_localizedValues[localeCode]['notification_threshold'] ??
_localizedValues['en']['notification_threshold'];
// STARTER: lang field - do not remove comment // STARTER: lang field - do not remove comment
String lookup(String key) { String lookup(String key) {