Recurring invoices
This commit is contained in:
parent
70e71df729
commit
b49332b2e5
|
|
@ -68,7 +68,6 @@ class InvoiceFields {
|
|||
static const String privateNotes = 'private_notes';
|
||||
static const String isRecurring = 'is_recurring';
|
||||
static const String frequencyId = 'frequency_id';
|
||||
static const String startDate = 'start_date';
|
||||
static const String endDate = 'end_date';
|
||||
static const String documents = 'documents';
|
||||
static const String customValue1 = 'custom1';
|
||||
|
|
@ -169,7 +168,6 @@ abstract class InvoiceEntity extends Object
|
|||
nextSendDate: '',
|
||||
frequencyId: '',
|
||||
remainingCycles: 0,
|
||||
startDate: '',
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -371,10 +369,6 @@ abstract class InvoiceEntity extends Object
|
|||
@BuiltValueField(wireName: 'next_send_date')
|
||||
String get nextSendDate;
|
||||
|
||||
@nullable
|
||||
@BuiltValueField(wireName: 'start_date')
|
||||
String get startDate;
|
||||
|
||||
@nullable
|
||||
@BuiltValueField(wireName: 'remaining_cycles')
|
||||
int get remainingCycles;
|
||||
|
|
|
|||
|
|
@ -321,12 +321,6 @@ class _$InvoiceEntitySerializer implements StructuredSerializer<InvoiceEntity> {
|
|||
..add(serializers.serialize(object.frequencyId,
|
||||
specifiedType: const FullType(String)));
|
||||
}
|
||||
if (object.startDate != null) {
|
||||
result
|
||||
..add('start_date')
|
||||
..add(serializers.serialize(object.startDate,
|
||||
specifiedType: const FullType(String)));
|
||||
}
|
||||
if (object.remainingCycles != null) {
|
||||
result
|
||||
..add('remaining_cycles')
|
||||
|
|
@ -595,10 +589,6 @@ class _$InvoiceEntitySerializer implements StructuredSerializer<InvoiceEntity> {
|
|||
result.nextSendDate = serializers.deserialize(value,
|
||||
specifiedType: const FullType(String)) as String;
|
||||
break;
|
||||
case 'start_date':
|
||||
result.startDate = serializers.deserialize(value,
|
||||
specifiedType: const FullType(String)) as String;
|
||||
break;
|
||||
case 'remaining_cycles':
|
||||
result.remainingCycles = serializers.deserialize(value,
|
||||
specifiedType: const FullType(int)) as int;
|
||||
|
|
@ -1380,8 +1370,6 @@ class _$InvoiceEntity extends InvoiceEntity {
|
|||
@override
|
||||
final String nextSendDate;
|
||||
@override
|
||||
final String startDate;
|
||||
@override
|
||||
final int remainingCycles;
|
||||
@override
|
||||
final String invoiceId;
|
||||
|
|
@ -1468,7 +1456,6 @@ class _$InvoiceEntity extends InvoiceEntity {
|
|||
this.frequencyId,
|
||||
this.lastSentDate,
|
||||
this.nextSendDate,
|
||||
this.startDate,
|
||||
this.remainingCycles,
|
||||
this.invoiceId,
|
||||
this.filename,
|
||||
|
|
@ -1683,7 +1670,6 @@ class _$InvoiceEntity extends InvoiceEntity {
|
|||
frequencyId == other.frequencyId &&
|
||||
lastSentDate == other.lastSentDate &&
|
||||
nextSendDate == other.nextSendDate &&
|
||||
startDate == other.startDate &&
|
||||
remainingCycles == other.remainingCycles &&
|
||||
invoiceId == other.invoiceId &&
|
||||
filename == other.filename &&
|
||||
|
|
@ -1724,9 +1710,9 @@ class _$InvoiceEntity extends InvoiceEntity {
|
|||
$jc(
|
||||
$jc(
|
||||
$jc(
|
||||
$jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc(0, amount.hashCode), balance.hashCode), clientId.hashCode), statusId.hashCode), number.hashCode), discount.hashCode), poNumber.hashCode), date.hashCode), dueDate.hashCode), publicNotes.hashCode), privateNotes.hashCode), terms.hashCode), footer.hashCode), designId.hashCode), usesInclusiveTaxes.hashCode), taxName1.hashCode), taxRate1.hashCode), taxName2.hashCode), taxRate2.hashCode), taxName3.hashCode), taxRate3.hashCode), isAmountDiscount.hashCode), partial.hashCode), taxAmount.hashCode), partialDueDate.hashCode), hasTasks.hashCode), autoBill.hashCode), customValue1.hashCode), customValue2.hashCode), customValue3.hashCode), customValue4.hashCode), customSurcharge1.hashCode), customSurcharge2.hashCode), customSurcharge3.hashCode), customSurcharge4.hashCode), customTaxes1.hashCode), customTaxes2.hashCode), customTaxes3.hashCode), customTaxes4.hashCode), hasExpenses.hashCode), exchangeRate.hashCode), reminder1Sent.hashCode), reminder2Sent.hashCode), reminder3Sent.hashCode), reminderLastSent.hashCode), frequencyId.hashCode), lastSentDate.hashCode),
|
||||
nextSendDate.hashCode),
|
||||
startDate.hashCode),
|
||||
$jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc($jc(0, amount.hashCode), balance.hashCode), clientId.hashCode), statusId.hashCode), number.hashCode), discount.hashCode), poNumber.hashCode), date.hashCode), dueDate.hashCode), publicNotes.hashCode), privateNotes.hashCode), terms.hashCode), footer.hashCode), designId.hashCode), usesInclusiveTaxes.hashCode), taxName1.hashCode), taxRate1.hashCode), taxName2.hashCode), taxRate2.hashCode), taxName3.hashCode), taxRate3.hashCode), isAmountDiscount.hashCode), partial.hashCode), taxAmount.hashCode), partialDueDate.hashCode), hasTasks.hashCode), autoBill.hashCode), customValue1.hashCode), customValue2.hashCode), customValue3.hashCode), customValue4.hashCode), customSurcharge1.hashCode), customSurcharge2.hashCode), customSurcharge3.hashCode), customSurcharge4.hashCode), customTaxes1.hashCode), customTaxes2.hashCode), customTaxes3.hashCode), customTaxes4.hashCode), hasExpenses.hashCode), exchangeRate.hashCode), reminder1Sent.hashCode), reminder2Sent.hashCode), reminder3Sent.hashCode), reminderLastSent.hashCode), frequencyId.hashCode),
|
||||
lastSentDate.hashCode),
|
||||
nextSendDate.hashCode),
|
||||
remainingCycles.hashCode),
|
||||
invoiceId.hashCode),
|
||||
filename.hashCode),
|
||||
|
|
@ -1797,7 +1783,6 @@ class _$InvoiceEntity extends InvoiceEntity {
|
|||
..add('frequencyId', frequencyId)
|
||||
..add('lastSentDate', lastSentDate)
|
||||
..add('nextSendDate', nextSendDate)
|
||||
..add('startDate', startDate)
|
||||
..add('remainingCycles', remainingCycles)
|
||||
..add('invoiceId', invoiceId)
|
||||
..add('filename', filename)
|
||||
|
|
@ -2026,10 +2011,6 @@ class InvoiceEntityBuilder
|
|||
String get nextSendDate => _$this._nextSendDate;
|
||||
set nextSendDate(String nextSendDate) => _$this._nextSendDate = nextSendDate;
|
||||
|
||||
String _startDate;
|
||||
String get startDate => _$this._startDate;
|
||||
set startDate(String startDate) => _$this._startDate = startDate;
|
||||
|
||||
int _remainingCycles;
|
||||
int get remainingCycles => _$this._remainingCycles;
|
||||
set remainingCycles(int remainingCycles) =>
|
||||
|
|
@ -2161,7 +2142,6 @@ class InvoiceEntityBuilder
|
|||
_frequencyId = _$v.frequencyId;
|
||||
_lastSentDate = _$v.lastSentDate;
|
||||
_nextSendDate = _$v.nextSendDate;
|
||||
_startDate = _$v.startDate;
|
||||
_remainingCycles = _$v.remainingCycles;
|
||||
_invoiceId = _$v.invoiceId;
|
||||
_filename = _$v.filename;
|
||||
|
|
@ -2251,7 +2231,6 @@ class InvoiceEntityBuilder
|
|||
frequencyId: frequencyId,
|
||||
lastSentDate: lastSentDate,
|
||||
nextSendDate: nextSendDate,
|
||||
startDate: startDate,
|
||||
remainingCycles: remainingCycles,
|
||||
invoiceId: invoiceId,
|
||||
filename: filename,
|
||||
|
|
|
|||
|
|
@ -59,6 +59,13 @@ class UpdateRecurringInvoice implements PersistUI {
|
|||
final InvoiceEntity recurringInvoice;
|
||||
}
|
||||
|
||||
class UpdateRecurringInvoiceClient implements PersistUI {
|
||||
UpdateRecurringInvoiceClient({this.client});
|
||||
|
||||
final ClientEntity client;
|
||||
}
|
||||
|
||||
|
||||
class LoadRecurringInvoice {
|
||||
LoadRecurringInvoice({this.completer, this.recurringInvoiceId});
|
||||
|
||||
|
|
@ -153,6 +160,12 @@ class AddQuoteItem implements PersistUI {
|
|||
final InvoiceItemEntity quoteItem;
|
||||
}
|
||||
|
||||
class AddRecurringInvoiceItem implements PersistUI {
|
||||
AddRecurringInvoiceItem({this.invoiceItem});
|
||||
|
||||
final InvoiceItemEntity invoiceItem;
|
||||
}
|
||||
|
||||
class AddRecurringInvoiceItems implements PersistUI {
|
||||
AddRecurringInvoiceItems(this.items);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/edit_scaffold.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/quote/edit/quote_edit_details_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/quote/edit/quote_edit_items_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/quote/edit/quote_edit_notes_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class RecurringInvoiceEdit extends StatefulWidget {
|
||||
|
|
@ -99,18 +101,25 @@ class _RecurringInvoiceEditState extends State<RecurringInvoiceEdit>
|
|||
],
|
||||
),
|
||||
body: Form(
|
||||
key: _formKey,
|
||||
child: Builder(builder: (BuildContext context) {
|
||||
return ListView(
|
||||
children: <Widget>[
|
||||
FormCard(
|
||||
children: <Widget>[
|
||||
// STARTER: widgets - do not remove comment
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
})),
|
||||
key: _formKey,
|
||||
child: state.prefState.isDesktop
|
||||
? QuoteEditDetailsScreen(
|
||||
viewModel: widget.viewModel,
|
||||
)
|
||||
: TabBarView(
|
||||
key: ValueKey('__quote_${viewModel.invoice.id}__'),
|
||||
controller: _controller,
|
||||
children: <Widget>[
|
||||
QuoteEditDetailsScreen(
|
||||
viewModel: widget.viewModel,
|
||||
),
|
||||
QuoteEditItemsScreen(
|
||||
viewModel: widget.viewModel,
|
||||
),
|
||||
QuoteEditNotesScreen(),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,111 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:built_collection/built_collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/screen_imports.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_desktop.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_details.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_details_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/recurring_invoice/edit/recurring_invoice_edit_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/money.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
|
||||
class RecurringInvoiceEditDetailsScreen extends StatelessWidget {
|
||||
const RecurringInvoiceEditDetailsScreen({Key key, @required this.viewModel})
|
||||
: super(key: key);
|
||||
|
||||
final EntityEditVM viewModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StoreConnector<AppState, RecurringInvoiceEditDetailsVM>(
|
||||
converter: (Store<AppState> store) {
|
||||
return RecurringInvoiceEditDetailsVM.fromStore(store);
|
||||
},
|
||||
builder: (context, viewModel) {
|
||||
if (viewModel.state.prefState.isDesktop) {
|
||||
return InvoiceEditDesktop(
|
||||
viewModel: viewModel,
|
||||
entityViewModel: this.viewModel,
|
||||
key: ValueKey('__quote_${viewModel.invoice.id}__'),
|
||||
entityType: EntityType.quote,
|
||||
);
|
||||
} else {
|
||||
return InvoiceEditDetails(
|
||||
viewModel: viewModel,
|
||||
entityType: EntityType.quote,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RecurringInvoiceEditDetailsVM extends EntityEditDetailsVM {
|
||||
RecurringInvoiceEditDetailsVM({
|
||||
AppState state,
|
||||
CompanyEntity company,
|
||||
InvoiceEntity invoice,
|
||||
Function(InvoiceEntity) onChanged,
|
||||
Function(BuildContext, InvoiceEntity, ClientEntity) onClientChanged,
|
||||
BuiltMap<String, ClientEntity> clientMap,
|
||||
BuiltList<String> clientList,
|
||||
Function(BuildContext context, Completer<SelectableEntity> completer)
|
||||
onAddClientPressed,
|
||||
}) : super(
|
||||
state: state,
|
||||
company: company,
|
||||
invoice: invoice,
|
||||
onChanged: onChanged,
|
||||
onClientChanged: onClientChanged,
|
||||
clientMap: clientMap,
|
||||
clientList: clientList,
|
||||
onAddClientPressed: onAddClientPressed,
|
||||
);
|
||||
|
||||
factory RecurringInvoiceEditDetailsVM.fromStore(Store<AppState> store) {
|
||||
final AppState state = store.state;
|
||||
final quote = state.quoteUIState.editing;
|
||||
final company = state.company;
|
||||
|
||||
return RecurringInvoiceEditDetailsVM(
|
||||
state: state,
|
||||
company: company,
|
||||
invoice: quote,
|
||||
onChanged: (InvoiceEntity quote) => store.dispatch(UpdateRecurringInvoice(quote)),
|
||||
clientMap: state.clientState.map,
|
||||
clientList: state.clientState.list,
|
||||
onClientChanged: (context, quote, client) {
|
||||
if (client != null) {
|
||||
final exchangeRate = getExchangeRate(context,
|
||||
fromCurrencyId: company.currencyId,
|
||||
toCurrencyId: client.currencyId);
|
||||
store.dispatch(UpdateRecurringInvoice(
|
||||
quote.rebuild((b) => b..exchangeRate = exchangeRate)));
|
||||
}
|
||||
store.dispatch(UpdateRecurringInvoiceClient(client: client));
|
||||
},
|
||||
onAddClientPressed: (context, completer) {
|
||||
createEntity(
|
||||
context: context,
|
||||
entity: ClientEntity(),
|
||||
force: true,
|
||||
completer: completer,
|
||||
cancelCompleter: Completer<Null>()
|
||||
..future.then((_) {
|
||||
store.dispatch(UpdateCurrentRoute(RecurringInvoiceEditScreen.route));
|
||||
}));
|
||||
completer.future.then((SelectableEntity client) {
|
||||
store.dispatch(UpdateCurrentRoute(RecurringInvoiceEditScreen.route));
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_items.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_items_desktop.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_items_vm.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_vm.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
|
||||
class RecurringInvoiceEditItemsScreen extends StatelessWidget {
|
||||
const RecurringInvoiceEditItemsScreen({
|
||||
Key key,
|
||||
@required this.viewModel,
|
||||
}) : super(key: key);
|
||||
|
||||
final EntityEditVM viewModel;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StoreConnector<AppState, RecurringInvoiceEditItemsVM>(
|
||||
converter: (Store<AppState> store) {
|
||||
return RecurringInvoiceEditItemsVM.fromStore(store);
|
||||
},
|
||||
builder: (context, viewModel) {
|
||||
if (viewModel.state.prefState.isDesktop) {
|
||||
return InvoiceEditItemsDesktop(
|
||||
viewModel: viewModel,
|
||||
entityViewModel: this.viewModel,
|
||||
);
|
||||
} else {
|
||||
return InvoiceEditItems(
|
||||
viewModel: viewModel,
|
||||
entityViewModel: this.viewModel,
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RecurringInvoiceEditItemsVM extends EntityEditItemsVM {
|
||||
RecurringInvoiceEditItemsVM({
|
||||
AppState state,
|
||||
CompanyEntity company,
|
||||
InvoiceEntity invoice,
|
||||
int invoiceItemIndex,
|
||||
Function addLineItem,
|
||||
Function deleteLineItem,
|
||||
Function(int) onRemoveInvoiceItemPressed,
|
||||
Function onDoneInvoiceItemPressed,
|
||||
Function(InvoiceItemEntity, int) onChangedInvoiceItem,
|
||||
}) : super(
|
||||
state: state,
|
||||
company: company,
|
||||
invoice: invoice,
|
||||
addLineItem: addLineItem,
|
||||
deleteLineItem: deleteLineItem,
|
||||
invoiceItemIndex: invoiceItemIndex,
|
||||
onRemoveInvoiceItemPressed: onRemoveInvoiceItemPressed,
|
||||
onDoneInvoiceItemPressed: onDoneInvoiceItemPressed,
|
||||
onChangedInvoiceItem: onChangedInvoiceItem,
|
||||
);
|
||||
|
||||
factory RecurringInvoiceEditItemsVM.fromStore(Store<AppState> store) {
|
||||
return RecurringInvoiceEditItemsVM(
|
||||
state: store.state,
|
||||
company: store.state.company,
|
||||
invoice: store.state.quoteUIState.editing,
|
||||
invoiceItemIndex: store.state.quoteUIState.editingItemIndex,
|
||||
onRemoveInvoiceItemPressed: (index) =>
|
||||
store.dispatch(DeleteRecurringInvoiceItem(index)),
|
||||
onDoneInvoiceItemPressed: () =>
|
||||
store.dispatch(EditRecurringInvoiceItem()),
|
||||
onChangedInvoiceItem: (item, index) {
|
||||
final quote = store.state.quoteUIState.editing;
|
||||
if (index == quote.lineItems.length) {
|
||||
store.dispatch(AddRecurringInvoiceItem(invoiceItem: item));
|
||||
} else {
|
||||
store
|
||||
.dispatch(UpdateRecurringInvoiceItem(item: item, index: index));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_notes.dart';
|
||||
import 'package:invoiceninja_flutter/ui/invoice/edit/invoice_edit_notes_vm.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
|
||||
class RecurringInvoiceEditNotesScreen extends StatelessWidget {
|
||||
const RecurringInvoiceEditNotesScreen({Key key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StoreConnector<AppState, RecurringInvoiceEditNotesVM>(
|
||||
converter: (Store<AppState> store) {
|
||||
return RecurringInvoiceEditNotesVM.fromStore(store);
|
||||
},
|
||||
builder: (context, viewModel) {
|
||||
return InvoiceEditNotes(
|
||||
viewModel: viewModel,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class RecurringInvoiceEditNotesVM extends EntityEditNotesVM {
|
||||
RecurringInvoiceEditNotesVM({
|
||||
CompanyEntity company,
|
||||
InvoiceEntity invoice,
|
||||
Function(InvoiceEntity) onChanged,
|
||||
}) : super(
|
||||
company: company,
|
||||
invoice: invoice,
|
||||
onChanged: onChanged,
|
||||
);
|
||||
|
||||
factory RecurringInvoiceEditNotesVM.fromStore(Store<AppState> store) {
|
||||
final AppState state = store.state;
|
||||
final quote = state.quoteUIState.editing;
|
||||
|
||||
return RecurringInvoiceEditNotesVM(
|
||||
company: state.company,
|
||||
invoice: quote,
|
||||
onChanged: (InvoiceEntity quote) => store.dispatch(UpdateRecurringInvoice(quote)),
|
||||
);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue