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
					
				
					 3 changed files with 277 additions and 1 deletions
				
			
		
							
								
								
									
										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
 | 
			
		||||
#  Copyright © 2010-2022 Lance Edgar
 | 
			
		||||
#  Copyright © 2010-2024 Lance Edgar
 | 
			
		||||
#
 | 
			
		||||
#  This file is part of Rattail.
 | 
			
		||||
#
 | 
			
		||||
| 
						 | 
				
			
			@ -24,6 +24,14 @@
 | 
			
		|||
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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -44,14 +52,138 @@ class VendorViewSupplement(ViewSupplement):
 | 
			
		|||
        g.set_filter('quickbooks_terms', model.QuickbooksVendor.quickbooks_terms)
 | 
			
		||||
 | 
			
		||||
    def configure_form(self, f):
 | 
			
		||||
 | 
			
		||||
        # quickbooks_name
 | 
			
		||||
        f.append('quickbooks_name')
 | 
			
		||||
 | 
			
		||||
        # 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')
 | 
			
		||||
 | 
			
		||||
    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):
 | 
			
		||||
        model = self.model
 | 
			
		||||
        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):
 | 
			
		||||
    VendorViewSupplement.defaults(config)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue