Add support for Vendor -> Quickbooks Bank Accounts field
this feels a bit hacky yet, but had to introduce some new mechanisms to allow for extra template stuff while avoiding adding a new vendors/edit (etc.) template in this project, since we are using a view supplement..ugh
This commit is contained in:
parent
ddeb4545a6
commit
7e08dd0f89
86
tailbone_quickbooks/templates/vendors/quickbooks_bank_accounts_js.mako
vendored
Normal file
86
tailbone_quickbooks/templates/vendors/quickbooks_bank_accounts_js.mako
vendored
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
${form.component_studly}Data.quickbooksBankAccountShowDialog = false
|
||||||
|
${form.component_studly}Data.quickbooksBankAccountEditing = null
|
||||||
|
${form.component_studly}Data.quickbooksBankAccountStore = null
|
||||||
|
${form.component_studly}Data.quickbooksBankAccountNumber = null
|
||||||
|
${form.component_studly}Data.quickbooksBankAccountStoreOptions = ${json.dumps(store_options)|n}
|
||||||
|
|
||||||
|
${form.component_studly}.methods.quickbooksBankAccountCreate = function() {
|
||||||
|
this.quickbooksBankAccountEditing = null
|
||||||
|
this.quickbooksBankAccountStore = null
|
||||||
|
this.quickbooksBankAccountNumber = null
|
||||||
|
this.quickbooksBankAccountShowDialog = true
|
||||||
|
}
|
||||||
|
|
||||||
|
${form.component_studly}.methods.quickbooksBankAccountEdit = function(row) {
|
||||||
|
this.quickbooksBankAccountEditing = row
|
||||||
|
this.quickbooksBankAccountStore = row.store_uuid
|
||||||
|
this.quickbooksBankAccountNumber = row.account_number
|
||||||
|
this.quickbooksBankAccountShowDialog = true
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.$refs.quickbooksBankAccountNumber.focus()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
${form.component_studly}.computed.quickbooksBankAccountSaveDisabled = function(row) {
|
||||||
|
if (!this.quickbooksBankAccountStore) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if (!this.quickbooksBankAccountNumber) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
${form.component_studly}.computed.quickbooksBankAccountsFinal = function() {
|
||||||
|
return JSON.stringify(this.quickbooksBankAccountsData)
|
||||||
|
}
|
||||||
|
|
||||||
|
${form.component_studly}.methods.quickbooksBankAccountSave = function() {
|
||||||
|
if (this.quickbooksBankAccountEditing) {
|
||||||
|
this.quickbooksBankAccountEditing.store_uuid = this.quickbooksBankAccountStore
|
||||||
|
this.quickbooksBankAccountEditing.account_number = this.quickbooksBankAccountNumber
|
||||||
|
this.quickbooksBankAccountShowDialog = false
|
||||||
|
} else {
|
||||||
|
if (this.quickbooksBankAccountIsStoreDefined(this.quickbooksBankAccountStore)) {
|
||||||
|
alert("An account number is already defined for that store!")
|
||||||
|
} else {
|
||||||
|
this.quickbooksBankAccountsData.push({
|
||||||
|
store_uuid: this.quickbooksBankAccountStore,
|
||||||
|
store: this.quickbooksBankAccountGetStoreDisplay(this.quickbooksBankAccountStore),
|
||||||
|
account_number: this.quickbooksBankAccountNumber,
|
||||||
|
})
|
||||||
|
this.quickbooksBankAccountShowDialog = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
${form.component_studly}.methods.quickbooksBankAccountIsStoreDefined = function(uuid) {
|
||||||
|
for (let account of this.quickbooksBankAccountsData) {
|
||||||
|
if (account.store_uuid == uuid) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
${form.component_studly}.methods.quickbooksBankAccountGetStoreDisplay = function(uuid) {
|
||||||
|
for (let store of this.quickbooksBankAccountStoreOptions) {
|
||||||
|
if (store.uuid == uuid) {
|
||||||
|
return store.display
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
${form.component_studly}.methods.quickbooksBankAccountDelete = function(row) {
|
||||||
|
if (confirm("Really delete this account number?")) {
|
||||||
|
let i = this.quickbooksBankAccountsData.indexOf(row)
|
||||||
|
this.quickbooksBankAccountsData.splice(i, 1)
|
||||||
|
this.quickbooksBankAccountShowDialog = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
58
tailbone_quickbooks/templates/vendors/quickbooks_bank_accounts_widget.mako
vendored
Normal file
58
tailbone_quickbooks/templates/vendors/quickbooks_bank_accounts_widget.mako
vendored
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
## -*- coding: utf-8; -*-
|
||||||
|
|
||||||
|
<div style="display: flex;">
|
||||||
|
<span style="flex-grow: 1;"></span>
|
||||||
|
<b-button type="is-primary"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="plus"
|
||||||
|
@click="quickbooksBankAccountCreate()">
|
||||||
|
Add Account
|
||||||
|
</b-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
${grid.render_table_element(data_prop='quickbooksBankAccountsData')|n}
|
||||||
|
|
||||||
|
<b-modal has-modal-card
|
||||||
|
:active.sync="quickbooksBankAccountShowDialog">
|
||||||
|
<div class="modal-card">
|
||||||
|
|
||||||
|
<header class="modal-card-head">
|
||||||
|
<p class="modal-card-title">Quickbooks Bank Account</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section class="modal-card-body">
|
||||||
|
|
||||||
|
<b-field label="Store"
|
||||||
|
:type="{'is-danger': !quickbooksBankAccountStore}">
|
||||||
|
<b-select v-model="quickbooksBankAccountStore"
|
||||||
|
:disabled="quickbooksBankAccountEditing">
|
||||||
|
<option v-for="store in quickbooksBankAccountStoreOptions"
|
||||||
|
:key="store.uuid"
|
||||||
|
:value="store.uuid">
|
||||||
|
{{ store.display }}
|
||||||
|
</option>
|
||||||
|
</b-select>
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
<b-field label="Account Number"
|
||||||
|
:type="{'is-danger': !quickbooksBankAccountNumber}">
|
||||||
|
<b-input v-model="quickbooksBankAccountNumber"
|
||||||
|
ref="quickbooksBankAccountNumber" />
|
||||||
|
</b-field>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<footer class="modal-card-foot">
|
||||||
|
<b-button type="is-primary"
|
||||||
|
@click="quickbooksBankAccountSave()"
|
||||||
|
:disabled="quickbooksBankAccountSaveDisabled"
|
||||||
|
icon-pack="fas"
|
||||||
|
icon-left="save">
|
||||||
|
Update
|
||||||
|
</b-button>
|
||||||
|
<b-button @click="quickbooksBankAccountShowDialog = false">
|
||||||
|
Cancel
|
||||||
|
</b-button>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
</b-modal>
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2022 Lance Edgar
|
# Copyright © 2010-2024 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -24,6 +24,14 @@
|
||||||
Vendor views, w/ Quickbooks integration
|
Vendor views, w/ Quickbooks integration
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
import colander
|
||||||
|
from deform import widget as dfwidget
|
||||||
|
from pyramid.renderers import render
|
||||||
|
from webhelpers2.html import HTML, tags
|
||||||
|
|
||||||
|
from tailbone import grids
|
||||||
from tailbone.views import ViewSupplement
|
from tailbone.views import ViewSupplement
|
||||||
|
|
||||||
|
|
||||||
|
@ -44,14 +52,138 @@ class VendorViewSupplement(ViewSupplement):
|
||||||
g.set_filter('quickbooks_terms', model.QuickbooksVendor.quickbooks_terms)
|
g.set_filter('quickbooks_terms', model.QuickbooksVendor.quickbooks_terms)
|
||||||
|
|
||||||
def configure_form(self, f):
|
def configure_form(self, f):
|
||||||
|
|
||||||
|
# quickbooks_name
|
||||||
f.append('quickbooks_name')
|
f.append('quickbooks_name')
|
||||||
|
|
||||||
|
# quickbooks_bank_account
|
||||||
f.append('quickbooks_bank_account')
|
f.append('quickbooks_bank_account')
|
||||||
|
|
||||||
|
# quickbooks_bank_accounts
|
||||||
|
f.append('quickbooks_bank_accounts_')
|
||||||
|
f.set_renderer('quickbooks_bank_accounts_', self.render_quickbooks_bank_accounts)
|
||||||
|
f.set_node('quickbooks_bank_accounts_', BankAccounts())
|
||||||
|
f.set_widget('quickbooks_bank_accounts_', BankAccountsWidget())
|
||||||
|
|
||||||
|
# quickbooks_terms
|
||||||
f.append('quickbooks_terms')
|
f.append('quickbooks_terms')
|
||||||
|
|
||||||
|
def render_quickbooks_bank_accounts(self, vendor, field):
|
||||||
|
accounts = getattr(vendor, 'quickbooks_bank_accounts')
|
||||||
|
if accounts:
|
||||||
|
g = make_accounts_grid()
|
||||||
|
return HTML.literal(g.render_table_element(data_prop='quickbooksBankAccountsData'))
|
||||||
|
|
||||||
|
def objectify(self, vendor, form, data):
|
||||||
|
model = self.model
|
||||||
|
old_accounts = vendor.quickbooks_bank_accounts
|
||||||
|
new_accounts = data['quickbooks_bank_accounts_']
|
||||||
|
|
||||||
|
for new_account in new_accounts:
|
||||||
|
old_account = old_accounts.get(new_account['store_uuid'])
|
||||||
|
if old_account:
|
||||||
|
if old_account.account_number != new_account['account_number']:
|
||||||
|
old_account.account_number = new_account['account_number']
|
||||||
|
else:
|
||||||
|
account = model.QuickbooksVendorBankAccount()
|
||||||
|
account.store_uuid = new_account['store_uuid']
|
||||||
|
account.account_number = new_account['account_number']
|
||||||
|
vendor.quickbooks_bank_accounts[account.store_uuid] = account
|
||||||
|
self.Session.add(account)
|
||||||
|
|
||||||
|
final_store_uuids = set([a['store_uuid'] for a in new_accounts])
|
||||||
|
for old_account in list(vendor.quickbooks_bank_accounts.values()):
|
||||||
|
if old_account.store_uuid not in final_store_uuids:
|
||||||
|
self.Session.delete(old_account)
|
||||||
|
|
||||||
|
return vendor
|
||||||
|
|
||||||
|
def template_kwargs(self, **kwargs):
|
||||||
|
app = self.get_rattail_app()
|
||||||
|
form = kwargs.get('form')
|
||||||
|
if form:
|
||||||
|
|
||||||
|
# quickbooks bank accounts
|
||||||
|
vendor = kwargs['instance']
|
||||||
|
accounts = []
|
||||||
|
for account in vendor.quickbooks_bank_accounts.values():
|
||||||
|
store = account.store
|
||||||
|
accounts.append({
|
||||||
|
'uuid': account.uuid,
|
||||||
|
'store': f'{store.id} - {store.name}',
|
||||||
|
'store_uuid': store.uuid,
|
||||||
|
'store_id': store.id,
|
||||||
|
'store_name': store.name,
|
||||||
|
'account_number': account.account_number,
|
||||||
|
})
|
||||||
|
accounts.sort(key=lambda a: a['store_id'])
|
||||||
|
# nb. this is needed for widget *and* readonly template
|
||||||
|
form.set_json_data('quickbooksBankAccountsData', accounts)
|
||||||
|
# TODO: these are needed by widget
|
||||||
|
stores = []
|
||||||
|
for store in app.get_active_stores(self.Session()):
|
||||||
|
stores.append({
|
||||||
|
'uuid': store.uuid,
|
||||||
|
'display': f'{store.id} - {store.name}',
|
||||||
|
})
|
||||||
|
form.include_template('/vendors/quickbooks_bank_accounts_js.mako', {
|
||||||
|
'store_options': stores,
|
||||||
|
})
|
||||||
|
|
||||||
|
return kwargs
|
||||||
|
|
||||||
def get_version_child_classes(self):
|
def get_version_child_classes(self):
|
||||||
model = self.model
|
model = self.model
|
||||||
return [model.QuickbooksVendor]
|
return [model.QuickbooksVendor]
|
||||||
|
|
||||||
|
|
||||||
|
def make_accounts_grid():
|
||||||
|
g = grids.Grid('quickbooks_bank_accounts',
|
||||||
|
[], # empty data
|
||||||
|
columns=[
|
||||||
|
'store',
|
||||||
|
'account_number',
|
||||||
|
])
|
||||||
|
return g
|
||||||
|
|
||||||
|
|
||||||
|
class BankAccount(colander.MappingSchema):
|
||||||
|
|
||||||
|
store_uuid = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
account_number = colander.SchemaNode(colander.String())
|
||||||
|
|
||||||
|
|
||||||
|
class BankAccounts(colander.SequenceSchema):
|
||||||
|
|
||||||
|
account = BankAccount()
|
||||||
|
|
||||||
|
|
||||||
|
class BankAccountsWidget(dfwidget.Widget):
|
||||||
|
|
||||||
|
def serialize(self, field, cstruct, **kw):
|
||||||
|
g = make_accounts_grid()
|
||||||
|
|
||||||
|
g.main_actions.append(
|
||||||
|
grids.GridAction('edit', icon='edit',
|
||||||
|
click_handler='quickbooksBankAccountEdit(props.row)'))
|
||||||
|
|
||||||
|
g.main_actions.append(
|
||||||
|
grids.GridAction('delete', icon='trash',
|
||||||
|
click_handler='quickbooksBankAccountDelete(props.row)'))
|
||||||
|
|
||||||
|
widget = render('/vendors/quickbooks_bank_accounts_widget.mako', {
|
||||||
|
'grid': g,
|
||||||
|
})
|
||||||
|
|
||||||
|
return HTML.tag('div', c=[
|
||||||
|
HTML.literal(widget),
|
||||||
|
tags.hidden(field.name, **{':value': "quickbooksBankAccountsFinal"}),
|
||||||
|
])
|
||||||
|
|
||||||
|
def deserialize(self, field, pstruct):
|
||||||
|
return json.loads(pstruct)
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
VendorViewSupplement.defaults(config)
|
VendorViewSupplement.defaults(config)
|
||||||
|
|
Loading…
Reference in a new issue