Add QR IBAN

This commit is contained in:
Hillel Coren 2022-06-30 09:32:28 +03:00
parent 3cf9cbdfac
commit a222e93d0d
7 changed files with 277 additions and 162 deletions

View File

@ -171,6 +171,7 @@ const String kCurrencyEuro = '3';
const String kCountryUnitedStates = '840'; const String kCountryUnitedStates = '840';
const String kCountryCanada = '124'; const String kCountryCanada = '124';
const String kCountrySwitzerland = '756';
const String kInvoiceStatusViewed = '-3'; const String kInvoiceStatusViewed = '-3';
const String kInvoiceStatusUnpaid = '-2'; const String kInvoiceStatusUnpaid = '-2';

View File

@ -619,6 +619,8 @@ abstract class CompanyEntity extends Object
String get currencyId => settings.currencyId ?? kDefaultCurrencyId; String get currencyId => settings.currencyId ?? kDefaultCurrencyId;
bool get supportsQrIban => settings.countryId == kCountrySwitzerland;
// ignore: unused_element // ignore: unused_element
static void _initializeBuilder(CompanyEntityBuilder builder) => builder static void _initializeBuilder(CompanyEntityBuilder builder) => builder
..calculateExpenseTaxByAmount = false ..calculateExpenseTaxByAmount = false

View File

@ -885,6 +885,14 @@ abstract class SettingsEntity
@BuiltValueField(wireName: 'purchase_order_number_counter') @BuiltValueField(wireName: 'purchase_order_number_counter')
int get purchaseOrderNumberCounter; int get purchaseOrderNumberCounter;
@nullable
@BuiltValueField(wireName: 'qr_iban')
String get qrIban;
@nullable
@BuiltValueField(wireName: 'besr_id')
String get besrId;
bool get hasAddress => address1 != null && address1.isNotEmpty; bool get hasAddress => address1 != null && address1.isNotEmpty;
bool get hasLogo => companyLogo != null && companyLogo.isNotEmpty; bool get hasLogo => companyLogo != null && companyLogo.isNotEmpty;

File diff suppressed because one or more lines are too long

View File

@ -78,6 +78,8 @@ class _CompanyDetailsState extends State<CompanyDetails>
final _creditFooterController = TextEditingController(); final _creditFooterController = TextEditingController();
final _purchaseOrderTermsController = TextEditingController(); final _purchaseOrderTermsController = TextEditingController();
final _purchaseOrderFooterController = TextEditingController(); final _purchaseOrderFooterController = TextEditingController();
final _qrIbanController = TextEditingController();
final _besrIdController = TextEditingController();
List<TextEditingController> _controllers = []; List<TextEditingController> _controllers = [];
@ -126,6 +128,8 @@ class _CompanyDetailsState extends State<CompanyDetails>
_creditTermsController, _creditTermsController,
_purchaseOrderFooterController, _purchaseOrderFooterController,
_purchaseOrderTermsController, _purchaseOrderTermsController,
_qrIbanController,
_besrIdController,
]; ];
_controllers.forEach( _controllers.forEach(
@ -157,6 +161,8 @@ class _CompanyDetailsState extends State<CompanyDetails>
_creditTermsController.text = settings.defaultCreditTerms; _creditTermsController.text = settings.defaultCreditTerms;
_purchaseOrderFooterController.text = settings.defaultPurchaseOrderFooter; _purchaseOrderFooterController.text = settings.defaultPurchaseOrderFooter;
_purchaseOrderTermsController.text = settings.defaultPurchaseOrderTerms; _purchaseOrderTermsController.text = settings.defaultPurchaseOrderTerms;
_qrIbanController.text = settings.qrIban;
_besrIdController.text = settings.besrId;
_controllers.forEach( _controllers.forEach(
(dynamic controller) => controller.addListener(_onSettingsChanged)); (dynamic controller) => controller.addListener(_onSettingsChanged));
@ -200,7 +206,9 @@ class _CompanyDetailsState extends State<CompanyDetails>
..defaultCreditFooter = _creditFooterController.text.trim() ..defaultCreditFooter = _creditFooterController.text.trim()
..defaultCreditTerms = _creditTermsController.text.trim() ..defaultCreditTerms = _creditTermsController.text.trim()
..defaultPurchaseOrderFooter = _purchaseOrderFooterController.text.trim() ..defaultPurchaseOrderFooter = _purchaseOrderFooterController.text.trim()
..defaultPurchaseOrderTerms = _purchaseOrderTermsController.text.trim()); ..defaultPurchaseOrderTerms = _purchaseOrderTermsController.text.trim()
..qrIban = _qrIbanController.text.trim()
..besrId = _besrIdController.text.trim());
if (settings != widget.viewModel.settings) { if (settings != widget.viewModel.settings) {
_debouncer.run(() { _debouncer.run(() {
widget.viewModel.onSettingsChanged(settings); widget.viewModel.onSettingsChanged(settings);
@ -323,6 +331,23 @@ class _CompanyDetailsState extends State<CompanyDetails>
), ),
], ],
), ),
if (company.supportsQrIban)
FormCard(
children: [
DecoratedFormField(
label: localization.qrIban,
controller: _qrIbanController,
onSavePressed: viewModel.onSavePressed,
keyboardType: TextInputType.text,
),
DecoratedFormField(
label: localization.besrId,
controller: _besrIdController,
onSavePressed: viewModel.onSavePressed,
keyboardType: TextInputType.text,
),
],
),
if (!state.settingsUIState.isFiltered) if (!state.settingsUIState.isFiltered)
FormCard( FormCard(
isLast: true, isLast: true,

View File

@ -62,10 +62,25 @@ class _InvoiceDesignState extends State<InvoiceDesign>
@override @override
void initState() { void initState() {
super.initState(); super.initState();
final settingsUIState = widget.viewModel.state.settingsUIState; final state = widget.viewModel.state;
final settingsUIState = state.settingsUIState;
_focusNode = FocusScopeNode(); _focusNode = FocusScopeNode();
int tabs = 6;
[
EntityType.invoice,
EntityType.quote,
EntityType.credit,
EntityType.task,
].forEach((entityType) {
if (state.company.isModuleEnabled(entityType)) {
tabs++;
}
});
_controller = TabController( _controller = TabController(
vsync: this, length: 10, initialIndex: settingsUIState.tabIndex); vsync: this, length: tabs, initialIndex: settingsUIState.tabIndex);
_controller.addListener(_onTabChanged); _controller.addListener(_onTabChanged);
} }
@ -110,11 +125,15 @@ class _InvoiceDesignState extends State<InvoiceDesign>
Tab(text: localization.clientDetails), Tab(text: localization.clientDetails),
Tab(text: localization.companyDetails), Tab(text: localization.companyDetails),
Tab(text: localization.companyAddress), Tab(text: localization.companyAddress),
Tab(text: localization.invoiceDetails), if (company.isModuleEnabled(EntityType.invoice))
Tab(text: localization.quoteDetails), Tab(text: localization.invoiceDetails),
Tab(text: localization.creditDetails), if (company.isModuleEnabled(EntityType.quote))
Tab(text: localization.quoteDetails),
if (company.isModuleEnabled(EntityType.credit))
Tab(text: localization.creditDetails),
Tab(text: localization.productColumns), Tab(text: localization.productColumns),
Tab(text: localization.taskColumns), if (company.isModuleEnabled(EntityType.task))
Tab(text: localization.taskColumns),
Tab(text: localization.totalFields), Tab(text: localization.totalFields),
], ],
), ),
@ -579,119 +598,123 @@ class _InvoiceDesignState extends State<InvoiceDesign>
prefix: 'company', prefix: 'company',
), ),
), ),
FormCard( if (company.isModuleEnabled(EntityType.invoice))
isLast: true, FormCard(
child: MultiSelectList( isLast: true,
options: [ child: MultiSelectList(
...[ options: [
...[
InvoiceFields.number,
InvoiceFields.poNumber,
InvoiceFields.date,
InvoiceFields.dueDate,
InvoiceFields.amount,
InvoiceFields.balance,
InvoiceFields.balanceDue,
InvoiceFields.customValue1,
InvoiceFields.customValue2,
InvoiceFields.customValue3,
InvoiceFields.customValue4,
InvoiceFields.project,
InvoiceFields.vendor,
].map((field) => '\$invoice.$field'),
...[
ClientFields.balance,
].map((field) => '\$client.$field')
],
defaultSelected: [
InvoiceFields.number, InvoiceFields.number,
InvoiceFields.poNumber, InvoiceFields.poNumber,
InvoiceFields.date, InvoiceFields.date,
InvoiceFields.dueDate, InvoiceFields.dueDate,
InvoiceFields.amount, InvoiceFields.total,
InvoiceFields.balance,
InvoiceFields.balanceDue, InvoiceFields.balanceDue,
InvoiceFields.customValue1, ].map((field) => '\$invoice.$field').toList(),
InvoiceFields.customValue2, selected:
InvoiceFields.customValue3, settings.getFieldsForSection(kPdfFieldsInvoiceDetails),
InvoiceFields.customValue4, onSelected: (values) {
InvoiceFields.project, viewModel.onSettingsChanged(settings.setFieldsForSection(
InvoiceFields.vendor, kPdfFieldsInvoiceDetails, values));
].map((field) => '\$invoice.$field'), },
...[ addTitle: localization.addField,
ClientFields.balance, liveChanges: true,
].map((field) => '\$client.$field') prefix: 'invoice',
], ),
defaultSelected: [
InvoiceFields.number,
InvoiceFields.poNumber,
InvoiceFields.date,
InvoiceFields.dueDate,
InvoiceFields.total,
InvoiceFields.balanceDue,
].map((field) => '\$invoice.$field').toList(),
selected: settings.getFieldsForSection(kPdfFieldsInvoiceDetails),
onSelected: (values) {
viewModel.onSettingsChanged(settings.setFieldsForSection(
kPdfFieldsInvoiceDetails, values));
},
addTitle: localization.addField,
liveChanges: true,
prefix: 'invoice',
), ),
), if (company.isModuleEnabled(EntityType.quote))
FormCard( FormCard(
isLast: true, isLast: true,
child: MultiSelectList( child: MultiSelectList(
options: [ options: [
...[ ...[
QuoteFields.number,
QuoteFields.poNumber,
QuoteFields.date,
QuoteFields.validUntil,
QuoteFields.total,
QuoteFields.customValue1,
QuoteFields.customValue2,
QuoteFields.customValue3,
QuoteFields.customValue4,
].map((field) => '\$quote.$field'),
...[
ClientFields.balance,
].map((field) => '\$client.$field')
],
defaultSelected: [
QuoteFields.number, QuoteFields.number,
QuoteFields.poNumber, QuoteFields.poNumber,
QuoteFields.date, QuoteFields.date,
QuoteFields.validUntil, QuoteFields.validUntil,
QuoteFields.total, QuoteFields.total,
QuoteFields.customValue1, ].map((field) => '\$quote.$field').toList(),
QuoteFields.customValue2, selected: settings.getFieldsForSection(kPdfFieldsQuoteDetails),
QuoteFields.customValue3, onSelected: (values) {
QuoteFields.customValue4, viewModel.onSettingsChanged(settings.setFieldsForSection(
].map((field) => '\$quote.$field'), kPdfFieldsQuoteDetails, values));
...[ },
ClientFields.balance, addTitle: localization.addField,
].map((field) => '\$client.$field') liveChanges: true,
], prefix: 'quote',
defaultSelected: [ ),
QuoteFields.number,
QuoteFields.poNumber,
QuoteFields.date,
QuoteFields.validUntil,
QuoteFields.total,
].map((field) => '\$quote.$field').toList(),
selected: settings.getFieldsForSection(kPdfFieldsQuoteDetails),
onSelected: (values) {
viewModel.onSettingsChanged(settings.setFieldsForSection(
kPdfFieldsQuoteDetails, values));
},
addTitle: localization.addField,
liveChanges: true,
prefix: 'quote',
), ),
), if (company.isModuleEnabled(EntityType.credit))
FormCard( FormCard(
isLast: true, isLast: true,
child: MultiSelectList( child: MultiSelectList(
options: [ options: [
...[ ...[
CreditFields.number,
CreditFields.poNumber,
CreditFields.date,
CreditFields.total,
CreditFields.balance,
CreditFields.customValue1,
CreditFields.customValue2,
CreditFields.customValue3,
CreditFields.customValue4,
].map((field) => '\$credit.$field'),
...[
ClientFields.balance,
].map((field) => '\$client.$field')
],
defaultSelected: [
CreditFields.number, CreditFields.number,
CreditFields.poNumber, CreditFields.poNumber,
CreditFields.date, CreditFields.date,
CreditFields.total,
CreditFields.balance, CreditFields.balance,
CreditFields.customValue1, CreditFields.total,
CreditFields.customValue2, ].map((field) => '\$credit.$field').toList(),
CreditFields.customValue3, selected: settings.getFieldsForSection(kPdfFieldsCreditDetails),
CreditFields.customValue4, onSelected: (values) {
].map((field) => '\$credit.$field'), viewModel.onSettingsChanged(settings.setFieldsForSection(
...[ kPdfFieldsCreditDetails, values));
ClientFields.balance, },
].map((field) => '\$client.$field') addTitle: localization.addField,
], liveChanges: true,
defaultSelected: [ prefix: 'credit',
CreditFields.number, ),
CreditFields.poNumber,
CreditFields.date,
CreditFields.balance,
CreditFields.total,
].map((field) => '\$credit.$field').toList(),
selected: settings.getFieldsForSection(kPdfFieldsCreditDetails),
onSelected: (values) {
viewModel.onSettingsChanged(settings.setFieldsForSection(
kPdfFieldsCreditDetails, values));
},
addTitle: localization.addField,
liveChanges: true,
prefix: 'credit',
), ),
),
FormCard( FormCard(
isLast: true, isLast: true,
child: MultiSelectList( child: MultiSelectList(
@ -728,42 +751,43 @@ class _InvoiceDesignState extends State<InvoiceDesign>
prefix: 'product', prefix: 'product',
), ),
), ),
FormCard( if (company.isModuleEnabled(EntityType.task))
isLast: true, FormCard(
child: MultiSelectList( isLast: true,
options: [ child: MultiSelectList(
TaskItemFields.service, options: [
TaskItemFields.description, TaskItemFields.service,
TaskItemFields.hours, TaskItemFields.description,
TaskItemFields.rate, TaskItemFields.hours,
if (company.hasItemTaxes) TaskItemFields.tax, TaskItemFields.rate,
if (company.enableProductDiscount) TaskItemFields.discount, if (company.hasItemTaxes) TaskItemFields.tax,
TaskItemFields.lineTotal, if (company.enableProductDiscount) TaskItemFields.discount,
TaskItemFields.custom1, TaskItemFields.lineTotal,
TaskItemFields.custom2, TaskItemFields.custom1,
TaskItemFields.custom3, TaskItemFields.custom2,
TaskItemFields.custom4, TaskItemFields.custom3,
TaskItemFields.grossLineTotal, TaskItemFields.custom4,
].map((field) => '\$task.$field').toList(), TaskItemFields.grossLineTotal,
defaultSelected: [ ].map((field) => '\$task.$field').toList(),
TaskItemFields.service, defaultSelected: [
TaskItemFields.description, TaskItemFields.service,
TaskItemFields.rate, TaskItemFields.description,
TaskItemFields.hours, TaskItemFields.rate,
if (company.enableProductDiscount) TaskItemFields.discount, TaskItemFields.hours,
if (company.hasItemTaxes) TaskItemFields.tax, if (company.enableProductDiscount) TaskItemFields.discount,
TaskItemFields.lineTotal, if (company.hasItemTaxes) TaskItemFields.tax,
].map((field) => '\$task.$field').toList(), TaskItemFields.lineTotal,
selected: settings.getFieldsForSection(kPdfFieldsTaskColumns), ].map((field) => '\$task.$field').toList(),
onSelected: (values) { selected: settings.getFieldsForSection(kPdfFieldsTaskColumns),
viewModel.onSettingsChanged(settings.setFieldsForSection( onSelected: (values) {
kPdfFieldsTaskColumns, values)); viewModel.onSettingsChanged(settings.setFieldsForSection(
}, kPdfFieldsTaskColumns, values));
addTitle: localization.addField, },
liveChanges: true, addTitle: localization.addField,
prefix: 'task', liveChanges: true,
prefix: 'task',
),
), ),
),
FormCard( FormCard(
isLast: true, isLast: true,
child: MultiSelectList( child: MultiSelectList(

View File

@ -16,6 +16,8 @@ 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
'qr_iban': 'QR IBAN',
'besr_id': 'BESR ID',
'accept': 'Accept', 'accept': 'Accept',
'clone_to_purchase_order': 'Clone to PO', 'clone_to_purchase_order': 'Clone to PO',
'vendor_email_not_set': 'Vendor does not have an email address set', 'vendor_email_not_set': 'Vendor does not have an email address set',
@ -70820,6 +70822,15 @@ mixin LocalizationsProvider on LocaleCodeAware {
_localizedValues[localeCode]['accept'] ?? _localizedValues[localeCode]['accept'] ??
_localizedValues['en']['accept']; _localizedValues['en']['accept'];
String get qrIban =>
_localizedValues[localeCode]['qr_iban'] ??
_localizedValues['en']['qr_iban'];
String get besrId =>
_localizedValues[localeCode]['besr_id'] ??
_localizedValues['en']['besr_id'];
// STARTER: lang field - do not remove comment // STARTER: lang field - do not remove comment
String lookup(String key) { String lookup(String key) {