Invoices
This commit is contained in:
parent
866e5dabb5
commit
dd24008091
|
|
@ -4,11 +4,10 @@ import 'package:invoiceninja/data/models/models.dart';
|
|||
import 'package:invoiceninja/ui/client/edit/client_edit_details.dart';
|
||||
import 'package:invoiceninja/ui/client/edit/client_edit_vm.dart';
|
||||
import 'package:invoiceninja/utils/localization.dart';
|
||||
|
||||
import '../../app/save_icon_button.dart';
|
||||
import 'client_edit_billing_address.dart';
|
||||
import 'client_edit_contacts.dart';
|
||||
import 'client_edit_shipping_address.dart';
|
||||
import 'package:invoiceninja/ui/app/save_icon_button.dart';
|
||||
import 'package:invoiceninja/ui/client/edit/client_edit_billing_address.dart';
|
||||
import 'package:invoiceninja/ui/client/edit/client_edit_contacts.dart';
|
||||
import 'package:invoiceninja/ui/client/edit/client_edit_shipping_address.dart';
|
||||
|
||||
class ClientEdit extends StatefulWidget {
|
||||
final ClientEditVM viewModel;
|
||||
|
|
|
|||
|
|
@ -4,8 +4,7 @@ import 'package:invoiceninja/data/models/models.dart';
|
|||
import 'package:invoiceninja/ui/client/edit/client_edit.dart';
|
||||
import 'package:invoiceninja/ui/client/edit/client_edit_vm.dart';
|
||||
import 'package:invoiceninja/utils/localization.dart';
|
||||
|
||||
import '../../app/form_card.dart';
|
||||
import 'package:invoiceninja/ui/app/form_card.dart';
|
||||
|
||||
class ClientEditDetails extends StatefulWidget {
|
||||
ClientEditDetails({
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ import 'package:flutter/material.dart';
|
|||
import 'package:invoiceninja/ui/app/actions_menu_button.dart';
|
||||
import 'package:invoiceninja/ui/app/form_card.dart';
|
||||
import 'package:invoiceninja/ui/app/progress_button.dart';
|
||||
import 'package:invoiceninja/ui/invoice/edit/invoice_edit_details.dart';
|
||||
import 'package:invoiceninja/ui/invoice/edit/invoice_edit_items.dart';
|
||||
import 'package:invoiceninja/ui/invoice/edit/invoice_edit_vm.dart';
|
||||
import 'package:invoiceninja/utils/localization.dart';
|
||||
|
||||
import '../../app/save_icon_button.dart';
|
||||
import 'package:invoiceninja/ui/app/save_icon_button.dart';
|
||||
|
||||
class InvoiceEdit extends StatefulWidget {
|
||||
final InvoiceEditVM viewModel;
|
||||
|
|
@ -20,100 +21,89 @@ class InvoiceEdit extends StatefulWidget {
|
|||
_InvoiceEditState createState() => _InvoiceEditState();
|
||||
}
|
||||
|
||||
class _InvoiceEditState extends State<InvoiceEdit> {
|
||||
class _InvoiceEditState extends State<InvoiceEdit>
|
||||
with SingleTickerProviderStateMixin {
|
||||
TabController _controller;
|
||||
static final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
static final GlobalKey<InvoiceEditDetailsState> _detailsKey =
|
||||
GlobalKey<InvoiceEditDetailsState>();
|
||||
static final GlobalKey<InvoiceEditItemsState> _itemsKey =
|
||||
GlobalKey<InvoiceEditItemsState>();
|
||||
|
||||
String _invoiceKey;
|
||||
String _notes;
|
||||
double _cost;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = new TabController(vsync: this, length: 2);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var viewModel = widget.viewModel;
|
||||
var localization = AppLocalization.of(context);
|
||||
var invoice = widget.viewModel.invoice;
|
||||
|
||||
List<Widget> editors = [
|
||||
InvoiceEditDetails(
|
||||
invoice: invoice,
|
||||
key: _detailsKey,
|
||||
),
|
||||
InvoiceEditItems(
|
||||
invoice: invoice,
|
||||
key: _itemsKey,
|
||||
),
|
||||
];
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(viewModel.invoice.isNew()
|
||||
? AppLocalization.of(context).newInvoice
|
||||
: viewModel.invoice.invoiceNumber),
|
||||
title: Text(invoice.isNew()
|
||||
? localization.newInvoice
|
||||
: invoice.invoiceNumber),
|
||||
actions: <Widget>[
|
||||
Builder(builder: (BuildContext context) {
|
||||
return SaveIconButton(
|
||||
isLoading: viewModel.isLoading,
|
||||
onPressed: () {
|
||||
if (!_formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
SaveIconButton(
|
||||
isLoading: widget.viewModel.isLoading,
|
||||
onPressed: () {
|
||||
if (! _formKey.currentState.validate()) {
|
||||
return;
|
||||
}
|
||||
|
||||
_formKey.currentState.save();
|
||||
/*
|
||||
viewModel.onSaveClicked(
|
||||
context,
|
||||
viewModel.invoice.rebuild((b) => b
|
||||
..invoiceNumber= _invoiceKey
|
||||
..notes = _notes
|
||||
..cost = _cost));
|
||||
*/
|
||||
},
|
||||
);
|
||||
}),
|
||||
viewModel.invoice.isNew()
|
||||
? Container()
|
||||
: ActionMenuButton(
|
||||
entity: viewModel.invoice,
|
||||
onSelected: viewModel.onActionSelected,
|
||||
)
|
||||
_formKey.currentState.save();
|
||||
|
||||
var detailsState = _detailsKey.currentState;
|
||||
var itemsState = _itemsKey.currentState;
|
||||
|
||||
/*
|
||||
InvoiceEntity invoice = widget.viewModel.invoice.rebuild((b) => b
|
||||
..items.replace(
|
||||
itemState?.getItems() ?? widget.viewModel.invoice.items));
|
||||
*/
|
||||
|
||||
widget.viewModel.onSaveClicked(context, invoice);
|
||||
},
|
||||
)
|
||||
],
|
||||
bottom: TabBar(
|
||||
controller: _controller,
|
||||
//isScrollable: true,
|
||||
tabs: [
|
||||
Tab(
|
||||
text: localization.details,
|
||||
),
|
||||
Tab(
|
||||
text: localization.items,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: <Widget>[
|
||||
FormCard(
|
||||
children: <Widget>[
|
||||
/*
|
||||
TextFormField(
|
||||
autocorrect: false,
|
||||
onSaved: (value) {
|
||||
_invoiceKey = value;
|
||||
},
|
||||
initialValue: viewModel.invoice.invoiceKey,
|
||||
decoration: InputDecoration(
|
||||
//border: InputBorder.none,
|
||||
labelText: AppLocalization.of(context).invoice,
|
||||
),
|
||||
validator: (val) => val.isEmpty || val.trim().length == 0
|
||||
? AppLocalization.of(context).pleaseEnterAInvoiceKey
|
||||
: null,
|
||||
),
|
||||
TextFormField(
|
||||
initialValue: viewModel.invoice.notes,
|
||||
onSaved: (value) {
|
||||
_notes = value;
|
||||
},
|
||||
maxLines: 4,
|
||||
decoration: InputDecoration(
|
||||
labelText: AppLocalization.of(context).notes,
|
||||
),
|
||||
),
|
||||
TextFormField(
|
||||
initialValue: viewModel.invoice.cost == null ||
|
||||
viewModel.invoice.cost == 0.0
|
||||
? null
|
||||
: viewModel.invoice.cost.toStringAsFixed(2),
|
||||
onSaved: (value) {
|
||||
_cost = double.tryParse(value) ?? 0.0;
|
||||
},
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: InputDecoration(
|
||||
//border: InputBorder.none,
|
||||
labelText: AppLocalization.of(context).cost,
|
||||
),
|
||||
),
|
||||
*/
|
||||
],
|
||||
),
|
||||
],
|
||||
child: TabBarView(
|
||||
controller: _controller,
|
||||
children: editors,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja/data/models/models.dart';
|
||||
import 'package:invoiceninja/ui/app/form_card.dart';
|
||||
import 'package:invoiceninja/utils/localization.dart';
|
||||
|
||||
class InvoiceEditDetails extends StatefulWidget {
|
||||
InvoiceEditDetails({
|
||||
Key key,
|
||||
@required this.invoice,
|
||||
}) : super(key: key);
|
||||
|
||||
final InvoiceEntity invoice;
|
||||
|
||||
@override
|
||||
InvoiceEditDetailsState createState() => new InvoiceEditDetailsState();
|
||||
}
|
||||
|
||||
class InvoiceEditDetailsState extends State<InvoiceEditDetails>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
int clientId;
|
||||
String invoiceDate;
|
||||
String dueDate;
|
||||
double partial;
|
||||
String partialDate;
|
||||
String invoiceNumber;
|
||||
String poNumber;
|
||||
double discount;
|
||||
bool isAmountDiscount;
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var localization = AppLocalization.of(context);
|
||||
var invoice = widget.invoice;
|
||||
|
||||
return ListView(
|
||||
shrinkWrap: true,
|
||||
children: <Widget>[
|
||||
invoice.isNew()
|
||||
? Container()
|
||||
: TextFormField(
|
||||
autocorrect: false,
|
||||
onSaved: (value) => invoiceNumber = value.trim(),
|
||||
initialValue: invoice.invoiceNumber,
|
||||
decoration: InputDecoration(
|
||||
labelText: localization.invoiceNumber,
|
||||
),
|
||||
),
|
||||
TextFormField(
|
||||
autocorrect: false,
|
||||
onSaved: (value) => poNumber = value.trim(),
|
||||
initialValue: invoice.poNumber,
|
||||
decoration: InputDecoration(
|
||||
labelText: localization.poNumber,
|
||||
),
|
||||
),
|
||||
TextFormField(
|
||||
autocorrect: false,
|
||||
onSaved: (value) => discount = double.tryParse(value) ?? 0.0,
|
||||
initialValue: invoice.discount.toStringAsFixed(2),
|
||||
decoration: InputDecoration(
|
||||
labelText: localization.discount,
|
||||
),
|
||||
),
|
||||
TextFormField(
|
||||
autocorrect: false,
|
||||
onSaved: (value) => partial = double.tryParse(value) ?? 0.0,
|
||||
initialValue: invoice.partial.toStringAsFixed(2),
|
||||
decoration: InputDecoration(
|
||||
labelText: localization.partial,
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,184 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja/data/models/models.dart';
|
||||
import 'package:invoiceninja/utils/localization.dart';
|
||||
import 'package:invoiceninja/ui/app/form_card.dart';
|
||||
|
||||
class InvoiceEditItems extends StatefulWidget {
|
||||
InvoiceEditItems({
|
||||
Key key,
|
||||
@required this.invoice,
|
||||
}) : super(key: key);
|
||||
|
||||
final InvoiceEntity invoice;
|
||||
|
||||
@override
|
||||
InvoiceEditItemsState createState() => new InvoiceEditItemsState();
|
||||
}
|
||||
|
||||
class InvoiceEditItemsState extends State<InvoiceEditItems>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
|
||||
List<InvoiceItemEntity> invoiceItems;
|
||||
List<GlobalKey<ItemEditDetailsState>> invoiceItemKeys;
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
var invoice = widget.invoice;
|
||||
invoiceItems = invoice.invoiceItems.toList();
|
||||
invoiceItemKeys = invoice.invoiceItems
|
||||
.map((invoiceItem) => GlobalKey<ItemEditDetailsState>())
|
||||
.toList();
|
||||
}
|
||||
|
||||
List<InvoiceItemEntity> getItems() {
|
||||
List<InvoiceItemEntity> invoiceItems = [];
|
||||
invoiceItemKeys.forEach((invoiceItemKey) {
|
||||
if (invoiceItemKey.currentState != null) {
|
||||
invoiceItems.add(invoiceItemKey.currentState.getItem());
|
||||
}
|
||||
});
|
||||
return invoiceItems;
|
||||
}
|
||||
|
||||
_onAddPressed() {
|
||||
setState(() {
|
||||
invoiceItems.add(InvoiceItemEntity());
|
||||
invoiceItemKeys.add(GlobalKey<ItemEditDetailsState>());
|
||||
});
|
||||
}
|
||||
|
||||
_onRemovePressed(GlobalKey<ItemEditDetailsState> key) {
|
||||
setState(() {
|
||||
var index = invoiceItemKeys.indexOf(key);
|
||||
invoiceItemKeys.removeAt(index);
|
||||
invoiceItems.removeAt(index);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var localization = AppLocalization.of(context);
|
||||
List<Widget> invoiceItems = [];
|
||||
|
||||
for (var i = 0; i < invoiceItems.length; i++) {
|
||||
var invoiceItem = invoiceItems[i];
|
||||
var invoiceItemKey = invoiceItemKeys[i];
|
||||
invoiceItems.add(ItemEditDetails(
|
||||
//invoiceItem: invoiceItem,
|
||||
key: invoiceItemKey,
|
||||
onRemovePressed: (key) => _onRemovePressed(key),
|
||||
isRemoveVisible: invoiceItems.length > 1,
|
||||
));
|
||||
}
|
||||
|
||||
invoiceItems.add(Padding(
|
||||
padding: const EdgeInsets.only(left: 16.0, right: 16.0, top: 4.0),
|
||||
child: RaisedButton(
|
||||
elevation: 4.0,
|
||||
color: Theme.of(context).primaryColor,
|
||||
textColor: Theme.of(context).secondaryHeaderColor,
|
||||
//child: Text(localization.addItem.toUpperCase()),
|
||||
onPressed: _onAddPressed,
|
||||
),
|
||||
));
|
||||
|
||||
return ListView(
|
||||
children: invoiceItems,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ItemEditDetails extends StatefulWidget {
|
||||
ItemEditDetails({
|
||||
Key key,
|
||||
@required this.invoiceItem,
|
||||
@required this.onRemovePressed,
|
||||
@required this.isRemoveVisible,
|
||||
}) : super(key: key);
|
||||
|
||||
final InvoiceItemEntity invoiceItem;
|
||||
final Function(GlobalKey<ItemEditDetailsState>) onRemovePressed;
|
||||
final bool isRemoveVisible;
|
||||
|
||||
@override
|
||||
ItemEditDetailsState createState() => ItemEditDetailsState();
|
||||
}
|
||||
|
||||
class ItemEditDetailsState extends State<ItemEditDetails> {
|
||||
String _firstName;
|
||||
String _lastName;
|
||||
String _email;
|
||||
String _phone;
|
||||
|
||||
InvoiceItemEntity getItem() {
|
||||
return widget.invoiceItem.rebuild((b) => b
|
||||
//..phone = _phone
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var localization = AppLocalization.of(context);
|
||||
|
||||
_confirmDelete() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
semanticLabel: localization.areYouSure,
|
||||
title: Text(localization.areYouSure),
|
||||
actions: <Widget>[
|
||||
new FlatButton(
|
||||
child: Text(localization.cancel.toUpperCase()),
|
||||
onPressed: () {
|
||||
Navigator.pop(context);
|
||||
}),
|
||||
new FlatButton(
|
||||
child: Text(localization.ok.toUpperCase()),
|
||||
onPressed: () {
|
||||
widget.onRemovePressed(widget.key);
|
||||
Navigator.pop(context);
|
||||
})
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return FormCard(
|
||||
children: <Widget>[
|
||||
/*
|
||||
TextFormField(
|
||||
autocorrect: false,
|
||||
initialValue: widget.invoiceItem.firstName,
|
||||
onSaved: (value) => _firstName = value.trim(),
|
||||
decoration: InputDecoration(
|
||||
labelText: localization.firstName,
|
||||
),
|
||||
),
|
||||
*/
|
||||
widget.isRemoveVisible
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: <Widget>[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 14.0),
|
||||
child: FlatButton(
|
||||
child: Text(
|
||||
localization.remove,
|
||||
style: TextStyle(
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
),
|
||||
onPressed: _confirmDelete,
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
: Container(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,7 @@ import 'package:invoiceninja/ui/app/form_card.dart';
|
|||
import 'package:invoiceninja/ui/app/progress_button.dart';
|
||||
import 'package:invoiceninja/ui/product/edit/product_edit_vm.dart';
|
||||
import 'package:invoiceninja/utils/localization.dart';
|
||||
|
||||
import '../../app/save_icon_button.dart';
|
||||
import 'package:invoiceninja/ui/app/save_icon_button.dart';
|
||||
|
||||
class ProductEdit extends StatefulWidget {
|
||||
final ProductEditVM viewModel;
|
||||
|
|
|
|||
|
|
@ -119,6 +119,8 @@ class AppLocalization {
|
|||
'quote_number': 'Quote Number',
|
||||
'quote_date': 'Quote Date',
|
||||
'valid_until': 'Valid Until',
|
||||
'items': 'Items',
|
||||
'partial': 'Partial/Deposit',
|
||||
|
||||
'payment': 'Payment',
|
||||
'payments': 'Payments',
|
||||
|
|
@ -230,6 +232,9 @@ class AppLocalization {
|
|||
String get quoteNumber => _localizedValues[locale.languageCode]['quote_number'];
|
||||
String get quoteDate => _localizedValues[locale.languageCode]['quote_date'];
|
||||
String get validUntil => _localizedValues[locale.languageCode]['valid_until'];
|
||||
String get items => _localizedValues[locale.languageCode]['items'];
|
||||
String get partial => _localizedValues[locale.languageCode]['partial'];
|
||||
|
||||
|
||||
String get payment => _localizedValues[locale.languageCode]['payment'];
|
||||
String get payments => _localizedValues[locale.languageCode]['payments'];
|
||||
|
|
|
|||
Loading…
Reference in New Issue