Fixed tests for Tablet/Desktop layout
This commit is contained in:
parent
fad460dc7b
commit
4557c7e1ec
|
|
@ -61,3 +61,29 @@ class ActionMenuButton extends StatelessWidget {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// This class is used to differentiate List and View ActionMenuButtons
|
||||
/// during tests
|
||||
class ViewActionMenuButton extends StatelessWidget {
|
||||
const ViewActionMenuButton({
|
||||
@required this.entity,
|
||||
@required this.onSelected,
|
||||
this.isSaving = false,
|
||||
this.entityActions,
|
||||
});
|
||||
|
||||
final BaseEntity entity;
|
||||
final List<EntityAction> entityActions;
|
||||
final Function(BuildContext, EntityAction) onSelected;
|
||||
final bool isSaving;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ActionMenuButton(
|
||||
entity: entity,
|
||||
onSelected: onSelected,
|
||||
isSaving: isSaving,
|
||||
entityActions: entityActions
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -335,6 +335,7 @@ class _AppBottomBarState extends State<AppBottomBar> {
|
|||
final prefState = store.state.prefState;
|
||||
final isList = prefState.moduleLayout == ModuleLayout.list ||
|
||||
widget.entityType.isSetting;
|
||||
final company = state.company;
|
||||
|
||||
return BottomAppBar(
|
||||
shape: CircularNotchedRectangle(),
|
||||
|
|
@ -384,7 +385,6 @@ class _AppBottomBarState extends State<AppBottomBar> {
|
|||
),
|
||||
if (widget.statuses.isNotEmpty)
|
||||
IconButton(
|
||||
tooltip: localization.filter,
|
||||
icon: Icon(Icons.filter),
|
||||
onPressed: _showFilterStatusSheet,
|
||||
color: store.state
|
||||
|
|
@ -395,7 +395,6 @@ class _AppBottomBarState extends State<AppBottomBar> {
|
|||
),
|
||||
if (widget.customValues1.isNotEmpty)
|
||||
IconButton(
|
||||
tooltip: localization.filter,
|
||||
icon: Icon(Icons.looks_one),
|
||||
onPressed: _showFilterCustom1Sheet,
|
||||
color: store.state
|
||||
|
|
@ -406,7 +405,6 @@ class _AppBottomBarState extends State<AppBottomBar> {
|
|||
),
|
||||
if (widget.customValues2.isNotEmpty)
|
||||
IconButton(
|
||||
tooltip: localization.filter,
|
||||
icon: Icon(Icons.looks_two),
|
||||
onPressed: _showFilterCustom2Sheet,
|
||||
color: store.state
|
||||
|
|
@ -417,7 +415,6 @@ class _AppBottomBarState extends State<AppBottomBar> {
|
|||
),
|
||||
if (widget.customValues3.isNotEmpty)
|
||||
IconButton(
|
||||
tooltip: localization.filter,
|
||||
icon: Icon(Icons.looks_two),
|
||||
onPressed: _showFilterCustom3Sheet,
|
||||
color: store.state
|
||||
|
|
@ -428,7 +425,6 @@ class _AppBottomBarState extends State<AppBottomBar> {
|
|||
),
|
||||
if (widget.customValues4.isNotEmpty)
|
||||
IconButton(
|
||||
tooltip: localization.filter,
|
||||
icon: Icon(Icons.looks_two),
|
||||
onPressed: _showFilterCustom4Sheet,
|
||||
color: store.state
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@ class _EntityDropdownState extends State<EntityDropdown> {
|
|||
alignment: Alignment.centerRight,
|
||||
children: <Widget>[
|
||||
TypeAheadFormField<String>(
|
||||
validator: widget.validator,
|
||||
noItemsFoundBuilder: (context) => SizedBox(),
|
||||
suggestionsBoxDecoration: SuggestionsBoxDecoration(
|
||||
constraints: BoxConstraints(
|
||||
|
|
|
|||
|
|
@ -243,60 +243,70 @@ class MenuDrawer extends StatelessWidget {
|
|||
entityType: EntityType.client,
|
||||
icon: getEntityIcon(EntityType.client),
|
||||
title: localization.clients,
|
||||
iconTooltip: localization.newClient,
|
||||
),
|
||||
DrawerTile(
|
||||
company: company,
|
||||
entityType: EntityType.product,
|
||||
icon: getEntityIcon(EntityType.product),
|
||||
title: localization.products,
|
||||
iconTooltip: localization.newProduct,
|
||||
),
|
||||
DrawerTile(
|
||||
company: company,
|
||||
entityType: EntityType.invoice,
|
||||
icon: getEntityIcon(EntityType.invoice),
|
||||
title: localization.invoices,
|
||||
iconTooltip: localization.newInvoice,
|
||||
),
|
||||
DrawerTile(
|
||||
company: company,
|
||||
entityType: EntityType.payment,
|
||||
icon: getEntityIcon(EntityType.payment),
|
||||
title: localization.payments,
|
||||
iconTooltip: localization.newPayment,
|
||||
),
|
||||
DrawerTile(
|
||||
company: company,
|
||||
entityType: EntityType.quote,
|
||||
icon: getEntityIcon(EntityType.quote),
|
||||
title: localization.quotes,
|
||||
iconTooltip: localization.newQuote,
|
||||
),
|
||||
DrawerTile(
|
||||
company: company,
|
||||
entityType: EntityType.credit,
|
||||
icon: getEntityIcon(EntityType.credit),
|
||||
title: localization.credits,
|
||||
iconTooltip: localization.newCredit,
|
||||
),
|
||||
DrawerTile(
|
||||
company: company,
|
||||
entityType: EntityType.project,
|
||||
icon: getEntityIcon(EntityType.project),
|
||||
title: localization.projects,
|
||||
iconTooltip: localization.newProject,
|
||||
),
|
||||
DrawerTile(
|
||||
company: company,
|
||||
entityType: EntityType.task,
|
||||
icon: getEntityIcon(EntityType.task),
|
||||
title: localization.tasks,
|
||||
iconTooltip: localization.newTask,
|
||||
),
|
||||
DrawerTile(
|
||||
company: company,
|
||||
entityType: EntityType.vendor,
|
||||
icon: getEntityIcon(EntityType.vendor),
|
||||
title: localization.vendors,
|
||||
iconTooltip: localization.newVendor,
|
||||
),
|
||||
DrawerTile(
|
||||
company: company,
|
||||
entityType: EntityType.expense,
|
||||
icon: getEntityIcon(EntityType.expense),
|
||||
title: localization.expenses,
|
||||
iconTooltip: localization.newExpense,
|
||||
),
|
||||
// STARTER: menu - do not remove comment
|
||||
DrawerTile(
|
||||
|
|
@ -349,6 +359,7 @@ class DrawerTile extends StatefulWidget {
|
|||
this.entityType,
|
||||
this.onLongPress,
|
||||
this.onCreateTap,
|
||||
this.iconTooltip,
|
||||
});
|
||||
|
||||
final CompanyEntity company;
|
||||
|
|
@ -358,6 +369,7 @@ class DrawerTile extends StatefulWidget {
|
|||
final Function onTap;
|
||||
final Function onLongPress;
|
||||
final Function onCreateTap;
|
||||
final String iconTooltip;
|
||||
|
||||
@override
|
||||
_DrawerTileState createState() => _DrawerTileState();
|
||||
|
|
@ -421,6 +433,7 @@ class _DrawerTileState extends State<DrawerTile> {
|
|||
);
|
||||
} else if (userCompany.canCreate(widget.entityType)) {
|
||||
trailingWidget = IconButton(
|
||||
tooltip: widget.iconTooltip,
|
||||
icon: Icon(
|
||||
Icons.add_circle_outline,
|
||||
color: textColor,
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class ViewScaffold extends StatelessWidget {
|
|||
);
|
||||
})
|
||||
: Container(),
|
||||
ActionMenuButton(
|
||||
ViewActionMenuButton(
|
||||
isSaving: state.isSaving,
|
||||
entity: entity,
|
||||
onSelected: (context, action) =>
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ class DashboardPanels extends StatelessWidget {
|
|||
currentData: currentData,
|
||||
previousData: previousData,
|
||||
isLoaded: isLoaded,
|
||||
title: AppLocalization.of(context).invoices);
|
||||
title: AppLocalization.of(context).invoiced);
|
||||
}
|
||||
|
||||
Widget _paymentChart(BuildContext context) {
|
||||
|
|
|
|||
|
|
@ -42,8 +42,6 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
|||
TabController _tabController;
|
||||
|
||||
FocusNode _focusNode;
|
||||
static final GlobalKey<FormState> _formKey =
|
||||
GlobalKey<FormState>(debugLabel: '_invoiceDesktopEdit');
|
||||
|
||||
final _invoiceNumberController = TextEditingController();
|
||||
final _poNumberController = TextEditingController();
|
||||
|
|
@ -173,232 +171,228 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
|||
final invoice = viewModel.invoice;
|
||||
final company = viewModel.company;
|
||||
|
||||
return AppForm(
|
||||
formKey: _formKey,
|
||||
focusNode: _focusNode,
|
||||
child: ListView(
|
||||
key: ValueKey('__invoice_${invoice.id}__'),
|
||||
children: <Widget>[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: FormCard(
|
||||
padding: const EdgeInsets.only(
|
||||
top: kMobileDialogPadding,
|
||||
right: kMobileDialogPadding / 2,
|
||||
bottom: kMobileDialogPadding,
|
||||
left: kMobileDialogPadding),
|
||||
children: <Widget>[
|
||||
if (invoice.isNew)
|
||||
ClientPicker(
|
||||
autofocus: kIsWeb,
|
||||
clientId: invoice.clientId,
|
||||
clientState: viewModel.state.clientState,
|
||||
onSelected: (client) =>
|
||||
viewModel.onClientChanged(invoice, client),
|
||||
onAddPressed: (completer) =>
|
||||
viewModel.onAddClientPressed(context, completer),
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: 200),
|
||||
child: InvoiceEditContactsScreen(),
|
||||
return ListView(
|
||||
key: ValueKey('__invoice_${invoice.id}__'),
|
||||
children: <Widget>[
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: FormCard(
|
||||
padding: const EdgeInsets.only(
|
||||
top: kMobileDialogPadding,
|
||||
right: kMobileDialogPadding / 2,
|
||||
bottom: kMobileDialogPadding,
|
||||
left: kMobileDialogPadding),
|
||||
children: <Widget>[
|
||||
if (invoice.isNew)
|
||||
ClientPicker(
|
||||
autofocus: kIsWeb,
|
||||
clientId: invoice.clientId,
|
||||
clientState: viewModel.state.clientState,
|
||||
onSelected: (client) =>
|
||||
viewModel.onClientChanged(invoice, client),
|
||||
onAddPressed: (completer) =>
|
||||
viewModel.onAddClientPressed(context, completer),
|
||||
),
|
||||
],
|
||||
),
|
||||
ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: 200),
|
||||
child: InvoiceEditContactsScreen(),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: FormCard(
|
||||
padding: const EdgeInsets.only(
|
||||
top: kMobileDialogPadding,
|
||||
right: kMobileDialogPadding / 2,
|
||||
bottom: kMobileDialogPadding,
|
||||
left: kMobileDialogPadding / 2),
|
||||
children: <Widget>[
|
||||
),
|
||||
Expanded(
|
||||
child: FormCard(
|
||||
padding: const EdgeInsets.only(
|
||||
top: kMobileDialogPadding,
|
||||
right: kMobileDialogPadding / 2,
|
||||
bottom: kMobileDialogPadding,
|
||||
left: kMobileDialogPadding / 2),
|
||||
children: <Widget>[
|
||||
DatePicker(
|
||||
validator: (String val) => val.trim().isEmpty
|
||||
? AppLocalization.of(context).pleaseSelectADate
|
||||
: null,
|
||||
labelText: widget.entityType == EntityType.credit
|
||||
? localization.creditDate
|
||||
: widget.entityType == EntityType.quote
|
||||
? localization.quoteDate
|
||||
: localization.invoiceDate,
|
||||
selectedDate: invoice.date,
|
||||
onSelected: (date) {
|
||||
viewModel
|
||||
.onChanged(invoice.rebuild((b) => b..date = date));
|
||||
},
|
||||
),
|
||||
if (widget.entityType != EntityType.credit)
|
||||
DatePicker(
|
||||
validator: (String val) => val.trim().isEmpty
|
||||
? AppLocalization.of(context).pleaseSelectADate
|
||||
: null,
|
||||
labelText: widget.entityType == EntityType.credit
|
||||
? localization.creditDate
|
||||
: widget.entityType == EntityType.quote
|
||||
? localization.quoteDate
|
||||
: localization.invoiceDate,
|
||||
selectedDate: invoice.date,
|
||||
allowClearing: true,
|
||||
labelText: widget.entityType == EntityType.quote
|
||||
? localization.validUntil
|
||||
: localization.dueDate,
|
||||
selectedDate: invoice.dueDate,
|
||||
onSelected: (date) {
|
||||
viewModel
|
||||
.onChanged(invoice.rebuild((b) => b..date = date));
|
||||
viewModel.onChanged(
|
||||
invoice.rebuild((b) => b..dueDate = date));
|
||||
},
|
||||
),
|
||||
if (widget.entityType != EntityType.credit)
|
||||
DatePicker(
|
||||
allowClearing: true,
|
||||
labelText: widget.entityType == EntityType.quote
|
||||
? localization.validUntil
|
||||
: localization.dueDate,
|
||||
selectedDate: invoice.dueDate,
|
||||
onSelected: (date) {
|
||||
viewModel.onChanged(
|
||||
invoice.rebuild((b) => b..dueDate = date));
|
||||
},
|
||||
),
|
||||
DecoratedFormField(
|
||||
label: localization.partialDeposit,
|
||||
controller: _partialController,
|
||||
keyboardType:
|
||||
TextInputType.numberWithOptions(decimal: true),
|
||||
DecoratedFormField(
|
||||
label: localization.partialDeposit,
|
||||
controller: _partialController,
|
||||
keyboardType:
|
||||
TextInputType.numberWithOptions(decimal: true),
|
||||
),
|
||||
if (invoice.partial != null && invoice.partial > 0)
|
||||
DatePicker(
|
||||
labelText: localization.partialDueDate,
|
||||
selectedDate: invoice.partialDueDate,
|
||||
onSelected: (date) {
|
||||
viewModel.onChanged(
|
||||
invoice.rebuild((b) => b..partialDueDate = date));
|
||||
},
|
||||
),
|
||||
if (invoice.partial != null && invoice.partial > 0)
|
||||
DatePicker(
|
||||
labelText: localization.partialDueDate,
|
||||
selectedDate: invoice.partialDueDate,
|
||||
onSelected: (date) {
|
||||
viewModel.onChanged(
|
||||
invoice.rebuild((b) => b..partialDueDate = date));
|
||||
},
|
||||
),
|
||||
CustomField(
|
||||
controller: _custom1Controller,
|
||||
field: CustomFieldType.invoice1,
|
||||
value: invoice.customValue1,
|
||||
),
|
||||
CustomField(
|
||||
controller: _custom3Controller,
|
||||
field: CustomFieldType.invoice3,
|
||||
value: invoice.customValue3,
|
||||
),
|
||||
],
|
||||
),
|
||||
CustomField(
|
||||
controller: _custom1Controller,
|
||||
field: CustomFieldType.invoice1,
|
||||
value: invoice.customValue1,
|
||||
),
|
||||
CustomField(
|
||||
controller: _custom3Controller,
|
||||
field: CustomFieldType.invoice3,
|
||||
value: invoice.customValue3,
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
child: FormCard(
|
||||
padding: const EdgeInsets.only(
|
||||
top: kMobileDialogPadding,
|
||||
right: kMobileDialogPadding,
|
||||
bottom: kMobileDialogPadding,
|
||||
left: kMobileDialogPadding / 2),
|
||||
children: <Widget>[
|
||||
DecoratedFormField(
|
||||
controller: _invoiceNumberController,
|
||||
label: widget.entityType == EntityType.credit
|
||||
? localization.creditNumber
|
||||
: widget.entityType == EntityType.quote
|
||||
? localization.quoteNumber
|
||||
: localization.invoiceNumber,
|
||||
validator: (String val) =>
|
||||
val.trim().isEmpty && invoice.isOld
|
||||
? AppLocalization.of(context)
|
||||
.pleaseEnterAnInvoiceNumber
|
||||
: null,
|
||||
),
|
||||
DecoratedFormField(
|
||||
label: localization.poNumber,
|
||||
controller: _poNumberController,
|
||||
),
|
||||
DiscountField(
|
||||
controller: _discountController,
|
||||
value: invoice.discount,
|
||||
isAmountDiscount: invoice.isAmountDiscount,
|
||||
onTypeChanged: (value) => viewModel.onChanged(
|
||||
invoice.rebuild((b) => b..isAmountDiscount = value)),
|
||||
),
|
||||
CustomField(
|
||||
controller: _custom2Controller,
|
||||
field: CustomFieldType.invoice2,
|
||||
value: invoice.customValue2,
|
||||
),
|
||||
CustomField(
|
||||
controller: _custom4Controller,
|
||||
field: CustomFieldType.invoice4,
|
||||
value: invoice.customValue4,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
child: FormCard(
|
||||
padding: const EdgeInsets.only(
|
||||
top: kMobileDialogPadding,
|
||||
right: kMobileDialogPadding,
|
||||
bottom: kMobileDialogPadding,
|
||||
left: kMobileDialogPadding / 2),
|
||||
children: <Widget>[
|
||||
DecoratedFormField(
|
||||
controller: _invoiceNumberController,
|
||||
label: widget.entityType == EntityType.credit
|
||||
? localization.creditNumber
|
||||
: widget.entityType == EntityType.quote
|
||||
? localization.quoteNumber
|
||||
: localization.invoiceNumber,
|
||||
validator: (String val) => val.trim().isEmpty &&
|
||||
invoice.isOld
|
||||
? AppLocalization.of(context).pleaseEnterAnInvoiceNumber
|
||||
: null,
|
||||
),
|
||||
DecoratedFormField(
|
||||
label: localization.poNumber,
|
||||
controller: _poNumberController,
|
||||
),
|
||||
DiscountField(
|
||||
controller: _discountController,
|
||||
value: invoice.discount,
|
||||
isAmountDiscount: invoice.isAmountDiscount,
|
||||
onTypeChanged: (value) => viewModel.onChanged(
|
||||
invoice.rebuild((b) => b..isAmountDiscount = value)),
|
||||
),
|
||||
CustomField(
|
||||
controller: _custom2Controller,
|
||||
field: CustomFieldType.invoice2,
|
||||
value: invoice.customValue2,
|
||||
),
|
||||
CustomField(
|
||||
controller: _custom4Controller,
|
||||
field: CustomFieldType.invoice4,
|
||||
value: invoice.customValue4,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
widget.entityType == EntityType.credit
|
||||
? CreditEditItemsScreen()
|
||||
: widget.entityType == EntityType.quote
|
||||
? QuoteEditItemsScreen()
|
||||
: InvoiceEditItemsScreen(),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: FormCard(
|
||||
padding: const EdgeInsets.only(
|
||||
top: kMobileDialogPadding,
|
||||
right: kMobileDialogPadding / 2,
|
||||
bottom: kMobileDialogPadding,
|
||||
left: kMobileDialogPadding),
|
||||
children: <Widget>[
|
||||
TabBar(
|
||||
),
|
||||
],
|
||||
),
|
||||
widget.entityType == EntityType.credit
|
||||
? CreditEditItemsScreen()
|
||||
: widget.entityType == EntityType.quote
|
||||
? QuoteEditItemsScreen()
|
||||
: InvoiceEditItemsScreen(),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: FormCard(
|
||||
padding: const EdgeInsets.only(
|
||||
top: kMobileDialogPadding,
|
||||
right: kMobileDialogPadding / 2,
|
||||
bottom: kMobileDialogPadding,
|
||||
left: kMobileDialogPadding),
|
||||
children: <Widget>[
|
||||
TabBar(
|
||||
controller: _tabController,
|
||||
tabs: [
|
||||
Tab(text: localization.publicNotes),
|
||||
Tab(text: localization.privateNotes),
|
||||
Tab(
|
||||
text: widget.entityType == EntityType.credit
|
||||
? localization.creditTerms
|
||||
: widget.entityType == EntityType.quote
|
||||
? localization.quoteTerms
|
||||
: localization.invoiceTerms),
|
||||
Tab(
|
||||
text: widget.entityType == EntityType.credit
|
||||
? localization.creditFooter
|
||||
: widget.entityType == EntityType.quote
|
||||
? localization.quoteFooter
|
||||
: localization.invoiceFooter),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 100,
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
tabs: [
|
||||
Tab(text: localization.publicNotes),
|
||||
Tab(text: localization.privateNotes),
|
||||
Tab(
|
||||
text: widget.entityType == EntityType.credit
|
||||
? localization.creditTerms
|
||||
: widget.entityType == EntityType.quote
|
||||
? localization.quoteTerms
|
||||
: localization.invoiceTerms),
|
||||
Tab(
|
||||
text: widget.entityType == EntityType.credit
|
||||
? localization.creditFooter
|
||||
: widget.entityType == EntityType.quote
|
||||
? localization.quoteFooter
|
||||
: localization.invoiceFooter),
|
||||
children: <Widget>[
|
||||
DecoratedFormField(
|
||||
maxLines: 4,
|
||||
controller: _publicNotesController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
label: '',
|
||||
),
|
||||
DecoratedFormField(
|
||||
maxLines: 4,
|
||||
controller: _privateNotesController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
label: '',
|
||||
),
|
||||
DecoratedFormField(
|
||||
maxLines: 4,
|
||||
controller: _termsController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
label: '',
|
||||
),
|
||||
DecoratedFormField(
|
||||
maxLines: 4,
|
||||
controller: _footerController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
label: '',
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: 100,
|
||||
child: TabBarView(
|
||||
controller: _tabController,
|
||||
children: <Widget>[
|
||||
DecoratedFormField(
|
||||
maxLines: 4,
|
||||
controller: _publicNotesController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
label: '',
|
||||
),
|
||||
DecoratedFormField(
|
||||
maxLines: 4,
|
||||
controller: _privateNotesController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
label: '',
|
||||
),
|
||||
DecoratedFormField(
|
||||
maxLines: 4,
|
||||
controller: _termsController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
label: '',
|
||||
),
|
||||
DecoratedFormField(
|
||||
maxLines: 4,
|
||||
controller: _footerController,
|
||||
keyboardType: TextInputType.multiline,
|
||||
label: '',
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: FormCard(
|
||||
padding: const EdgeInsets.only(
|
||||
top: kMobileDialogPadding,
|
||||
right: kMobileDialogPadding,
|
||||
bottom: kMobileDialogPadding,
|
||||
left: kMobileDialogPadding / 2),
|
||||
children: <Widget>[
|
||||
/*
|
||||
),
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: FormCard(
|
||||
padding: const EdgeInsets.only(
|
||||
top: kMobileDialogPadding,
|
||||
right: kMobileDialogPadding,
|
||||
bottom: kMobileDialogPadding,
|
||||
left: kMobileDialogPadding / 2),
|
||||
children: <Widget>[
|
||||
/*
|
||||
DecoratedFormField(
|
||||
controller: null,
|
||||
enabled: false,
|
||||
|
|
@ -406,60 +400,59 @@ class InvoiceEditDesktopState extends State<InvoiceEditDesktop>
|
|||
label: localization.subtotal,
|
||||
),
|
||||
*/
|
||||
UserPicker(
|
||||
userId: invoice.assignedUserId,
|
||||
onChanged: (userId) => viewModel.onChanged(
|
||||
invoice.rebuild((b) => b..assignedUserId = userId)),
|
||||
UserPicker(
|
||||
userId: invoice.assignedUserId,
|
||||
onChanged: (userId) => viewModel.onChanged(
|
||||
invoice.rebuild((b) => b..assignedUserId = userId)),
|
||||
),
|
||||
DesignPicker(
|
||||
initialValue: invoice.designId,
|
||||
onSelected: (value) => viewModel.onChanged(
|
||||
invoice.rebuild((b) => b..designId = value.id)),
|
||||
),
|
||||
CustomSurcharges(
|
||||
surcharge1Controller: _surcharge1Controller,
|
||||
surcharge2Controller: _surcharge2Controller,
|
||||
surcharge3Controller: _surcharge3Controller,
|
||||
surcharge4Controller: _surcharge4Controller,
|
||||
),
|
||||
if (company.settings.enableFirstInvoiceTaxRate)
|
||||
TaxRateDropdown(
|
||||
onSelected: (taxRate) =>
|
||||
viewModel.onChanged(invoice.applyTax(taxRate)),
|
||||
labelText: localization.tax,
|
||||
initialTaxName: invoice.taxName1,
|
||||
initialTaxRate: invoice.taxRate1,
|
||||
),
|
||||
DesignPicker(
|
||||
initialValue: invoice.designId,
|
||||
onSelected: (value) => viewModel.onChanged(
|
||||
invoice.rebuild((b) => b..designId = value.id)),
|
||||
if (company.settings.enableSecondInvoiceTaxRate)
|
||||
TaxRateDropdown(
|
||||
onSelected: (taxRate) => viewModel
|
||||
.onChanged(invoice.applyTax(taxRate, isSecond: true)),
|
||||
labelText: localization.tax,
|
||||
initialTaxName: invoice.taxName2,
|
||||
initialTaxRate: invoice.taxRate2,
|
||||
),
|
||||
CustomSurcharges(
|
||||
surcharge1Controller: _surcharge1Controller,
|
||||
surcharge2Controller: _surcharge2Controller,
|
||||
surcharge3Controller: _surcharge3Controller,
|
||||
surcharge4Controller: _surcharge4Controller,
|
||||
if (company.settings.enableThirdInvoiceTaxRate)
|
||||
TaxRateDropdown(
|
||||
onSelected: (taxRate) => viewModel
|
||||
.onChanged(invoice.applyTax(taxRate, isThird: true)),
|
||||
labelText: localization.tax,
|
||||
initialTaxName: invoice.taxName3,
|
||||
initialTaxRate: invoice.taxRate3,
|
||||
),
|
||||
if (company.settings.enableFirstInvoiceTaxRate)
|
||||
TaxRateDropdown(
|
||||
onSelected: (taxRate) =>
|
||||
viewModel.onChanged(invoice.applyTax(taxRate)),
|
||||
labelText: localization.tax,
|
||||
initialTaxName: invoice.taxName1,
|
||||
initialTaxRate: invoice.taxRate1,
|
||||
),
|
||||
if (company.settings.enableSecondInvoiceTaxRate)
|
||||
TaxRateDropdown(
|
||||
onSelected: (taxRate) => viewModel.onChanged(
|
||||
invoice.applyTax(taxRate, isSecond: true)),
|
||||
labelText: localization.tax,
|
||||
initialTaxName: invoice.taxName2,
|
||||
initialTaxRate: invoice.taxRate2,
|
||||
),
|
||||
if (company.settings.enableThirdInvoiceTaxRate)
|
||||
TaxRateDropdown(
|
||||
onSelected: (taxRate) => viewModel.onChanged(
|
||||
invoice.applyTax(taxRate, isThird: true)),
|
||||
labelText: localization.tax,
|
||||
initialTaxName: invoice.taxName3,
|
||||
initialTaxRate: invoice.taxRate3,
|
||||
),
|
||||
CustomSurcharges(
|
||||
surcharge1Controller: _surcharge1Controller,
|
||||
surcharge2Controller: _surcharge2Controller,
|
||||
surcharge3Controller: _surcharge3Controller,
|
||||
surcharge4Controller: _surcharge4Controller,
|
||||
isAfterTaxes: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
CustomSurcharges(
|
||||
surcharge1Controller: _surcharge1Controller,
|
||||
surcharge2Controller: _surcharge2Controller,
|
||||
surcharge3Controller: _surcharge3Controller,
|
||||
surcharge4Controller: _surcharge4Controller,
|
||||
isAfterTaxes: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
|
|||
Padding(
|
||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
||||
child: TypeAheadFormField<String>(
|
||||
key: ValueKey('__line_item_${index}_name__'),
|
||||
initialValue: lineItems[index].productKey,
|
||||
noItemsFoundBuilder: (context) => SizedBox(),
|
||||
suggestionsCallback: (pattern) {
|
||||
|
|
@ -238,6 +239,7 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
|
|||
Padding(
|
||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
||||
child: TextFormField(
|
||||
key: ValueKey('__line_item_${index}_description__'),
|
||||
initialValue: lineItems[index].notes,
|
||||
onChanged: (value) => viewModel.onChangedInvoiceItem(
|
||||
lineItems[index].rebuild((b) => b..notes = value),
|
||||
|
|
@ -346,6 +348,7 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
|
|||
Padding(
|
||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
||||
child: TextFormField(
|
||||
key: ValueKey('__line_item_${index}_cost__'),
|
||||
textAlign: TextAlign.right,
|
||||
initialValue: formatNumber(lineItems[index].cost, context,
|
||||
formatNumberType: FormatNumberType.input,
|
||||
|
|
@ -361,6 +364,7 @@ class _InvoiceEditItemsDesktopState extends State<InvoiceEditItemsDesktop> {
|
|||
Padding(
|
||||
padding: const EdgeInsets.only(right: kTableColumnGap),
|
||||
child: TextFormField(
|
||||
key: ValueKey('__line_item_${index}_quantity__'),
|
||||
textAlign: TextAlign.right,
|
||||
initialValue: formatNumber(
|
||||
lineItems[index].quantity, context,
|
||||
|
|
|
|||
|
|
@ -16,14 +16,16 @@ void runTestSuite({bool batchMode = false}) {
|
|||
|
||||
final clientName = makeUnique(faker.company.name());
|
||||
final poNumber =
|
||||
faker.randomGenerator.integer(999999, min: 100000).toString();
|
||||
faker.randomGenerator.integer(999999, min: 100000).toString();
|
||||
final productKey = makeUnique(faker.food.cuisine());
|
||||
final clientKey = faker.randomGenerator.integer(999999, min: 100000)
|
||||
.toString();
|
||||
final description = faker.lorem.sentences(5).toString();
|
||||
final cost =
|
||||
faker.randomGenerator.decimal(min: 50, scale: 10).toStringAsFixed(2);
|
||||
faker.randomGenerator.decimal(min: 50, scale: 10).toStringAsFixed(2);
|
||||
|
||||
final updatedPoNumber =
|
||||
faker.randomGenerator.integer(999999, min: 100000).toString();
|
||||
faker.randomGenerator.integer(999999, min: 100000).toString();
|
||||
|
||||
setUpAll(() async {
|
||||
localization = TestLocalization('en');
|
||||
|
|
@ -67,35 +69,53 @@ void runTestSuite({bool batchMode = false}) {
|
|||
|
||||
// Create a new invoice
|
||||
test('Add a new invoice', () async {
|
||||
|
||||
print('Tap new invoice');
|
||||
await driver.tap(find.byTooltip(localization.newInvoice));
|
||||
|
||||
print('Create new client: $clientName');
|
||||
await driver.tap(find.byValueKey(Keys.clientPickerEmptyKey));
|
||||
if(await isMobile(driver)) {
|
||||
await driver.tap(find.byValueKey(Keys.clientPickerEmptyKey));
|
||||
}
|
||||
await driver.tap(find.byTooltip(localization.createNew));
|
||||
|
||||
print('Fill the client form');
|
||||
await fillTextField(
|
||||
driver: driver, field: localization.name, value: clientName);
|
||||
await fillTextFields(driver, <String, String>{
|
||||
localization.name: clientName,
|
||||
localization.idNumber: clientKey
|
||||
});
|
||||
// Await for Debouncer
|
||||
await Future<dynamic>.delayed(Duration(milliseconds: 500));
|
||||
await driver.tap(find.text(localization.save));
|
||||
|
||||
// Await for Screen change
|
||||
await driver.waitFor(find.text(localization.newInvoice));
|
||||
|
||||
print('Fill the invoice form');
|
||||
await driver.tap(find.byTooltip(localization.addItem));
|
||||
await driver.tap(find.byTooltip(localization.createNew));
|
||||
if(await isMobile(driver)) {
|
||||
await driver.tap(find.byTooltip(localization.addItem));
|
||||
await driver.tap(find.byTooltip(localization.createNew));
|
||||
|
||||
await fillTextFields(driver, <String, String>{
|
||||
localization.product: productKey,
|
||||
localization.description: description,
|
||||
localization.unitCost: cost,
|
||||
localization.quantity: '1',
|
||||
});
|
||||
await fillTextFields(driver, <String, String>{
|
||||
localization.product: productKey,
|
||||
localization.description: description,
|
||||
localization.unitCost: cost,
|
||||
localization.quantity: '1',
|
||||
});
|
||||
|
||||
// Await for Debouncer
|
||||
await Future<dynamic>.delayed(Duration(milliseconds: 500));
|
||||
await driver.tap(find.text(localization.done));
|
||||
await driver.tap(find.text(localization.details));
|
||||
// Await for Debouncer
|
||||
await Future<dynamic>.delayed(Duration(milliseconds: 500));
|
||||
await driver.tap(find.text(localization.done));
|
||||
await driver.tap(find.text(localization.details));
|
||||
|
||||
} else {
|
||||
await fillTextFields(driver, <String, String>{
|
||||
getLineItemKey('name', 0): productKey,
|
||||
getLineItemKey('description', 0): description,
|
||||
getLineItemKey('cost', 0): cost,
|
||||
getLineItemKey('quantity', 0): '1'
|
||||
});
|
||||
}
|
||||
|
||||
await fillAndSaveForm(driver, <String, String>{
|
||||
localization.poNumber: poNumber,
|
||||
|
|
@ -140,7 +160,8 @@ void runTestSuite({bool batchMode = false}) {
|
|||
await selectAction(driver, localization.enterPayment);
|
||||
await driver.tap(find.text(localization.save));
|
||||
// "Completed" status
|
||||
await driver.waitFor(find.text(localization.paymentStatus4.toUpperCase()));
|
||||
await driver.waitFor(
|
||||
find.text(localization.paymentStatus4.toUpperCase()));
|
||||
|
||||
if (await isMobile(driver)) {
|
||||
await driver.tap(find.pageBack());
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import 'localizations.dart';
|
|||
class Keys {
|
||||
static const String openAppDrawer = 'Open navigation menu';
|
||||
static const String clientPickerEmptyKey = '__client___';
|
||||
static const String invoiceLineItemBaseKey = '__line_item';
|
||||
}
|
||||
|
||||
Future<bool> isTablet(FlutterDriver driver) async {
|
||||
|
|
@ -129,15 +130,15 @@ Future<void> fillAndSaveForm(FlutterDriver driver, Map<String, dynamic> values,
|
|||
// Await for Debouncer
|
||||
await Future<dynamic>.delayed(Duration(milliseconds: 400));
|
||||
|
||||
print('Check for updated values');
|
||||
await checkTextFields(driver, values, except: skipCheckFor);
|
||||
|
||||
print('Tap save');
|
||||
await driver.tap(find.text(localization.save));
|
||||
|
||||
// verify snackbar
|
||||
//await driver.waitFor(find.text(localization.updatedProduct));
|
||||
//await driver.tap(find.pageBack());
|
||||
|
||||
print('Check for updated values');
|
||||
await checkTextFields(driver, values, except: skipCheckFor);
|
||||
}
|
||||
|
||||
Future<void> testArchiveAndDelete(
|
||||
|
|
@ -146,32 +147,46 @@ Future<void> testArchiveAndDelete(
|
|||
String deletedMessage,
|
||||
String restoredMessage}) async {
|
||||
final localization = TestLocalization('en');
|
||||
final mobile = await isMobile(driver);
|
||||
|
||||
if (!mobile) {
|
||||
// Show archived and deleted entries on tablet/web
|
||||
await driver.tap(find.byTooltip(localization.filter));
|
||||
await driver.tap(find.text(localization.archived));
|
||||
await driver.tap(find.text(localization.deleted));
|
||||
await driver.tap(find.byTooltip(localization.filter));
|
||||
}
|
||||
|
||||
print('Archive record');
|
||||
await selectAction(driver, localization.archive);
|
||||
await driver.waitFor(find.text(archivedMessage));
|
||||
await driver.waitFor(find.text(localization.archived));
|
||||
//await driver.waitFor(find.text(localization.archived));
|
||||
|
||||
|
||||
print('Restore record');
|
||||
await selectAction(driver, localization.restore);
|
||||
await driver.waitFor(find.text(restoredMessage));
|
||||
await driver.waitForAbsent(find.text(localization.archived));
|
||||
await driver.waitForAbsent(find.byType('Snackbar'));
|
||||
|
||||
print('Delete record');
|
||||
await selectAction(driver, localization.delete);
|
||||
await driver.waitFor(find.text(deletedMessage));
|
||||
await driver.waitFor(find.text(localization.deleted));
|
||||
//await driver.waitFor(find.text(localization.deleted));
|
||||
|
||||
|
||||
print('Restore record');
|
||||
await selectAction(driver, localization.restore);
|
||||
await driver.waitFor(find.text(restoredMessage));
|
||||
await driver.waitForAbsent(find.text(localization.deleted));
|
||||
await driver.waitForAbsent(find.byType('Snackbar'));
|
||||
}
|
||||
|
||||
Future<void> selectAction(FlutterDriver driver, String action) async {
|
||||
await driver.tap(find.byType('ActionMenuButton'));
|
||||
await driver.tap(find.byType('ViewActionMenuButton'));
|
||||
await driver.tap(find.text(action));
|
||||
}
|
||||
|
||||
String makeUnique(String value) =>
|
||||
'$value ${faker.randomGenerator.integer(999999, min: 100000)}';
|
||||
|
||||
String getLineItemKey(String key, int index) =>
|
||||
'${Keys.invoiceLineItemBaseKey}_${index}_${key}__';
|
||||
|
|
|
|||
Loading…
Reference in New Issue