diff --git a/.gitignore b/.gitignore
index 2e6159c..ea1abef 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,4 @@
+*~
+*.pyc
+dist/
tailbone_quickbooks.egg-info/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 900982b..72cab08 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,16 @@ All notable changes to tailbone-quickbooks will be documented in this file.
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+## v0.2.0 (2024-06-11)
+
+### Feat
+
+- switch from setup.cfg to pyproject.toml + hatchling
+
+## [0.1.7] - 2024-06-03
+### Changed
+- Add support for Vendor -> Quickbooks Bank Accounts field.
+
## [0.1.6] - 2023-08-29
### Changed
- Mark exportable invoice as deleted, instead of actually deleting.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..8a0c37d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,11 @@
+
+# tailbone-quickbooks
+
+Rattail is a retail software framework, released under the GNU General
+Public License.
+
+This package contains software interfaces for
+[Quickbooks](https://quickbooks.intuit.com/).
+
+Please see the [Rattail Project](https://rattailproject.org/) for more
+information.
diff --git a/README.rst b/README.rst
deleted file mode 100644
index 343f4bf..0000000
--- a/README.rst
+++ /dev/null
@@ -1,14 +0,0 @@
-
-tailbone-quickbooks
-===================
-
-Rattail is a retail software framework, released under the GNU General
-Public License.
-
-This package contains software interfaces for `Quickbooks`_.
-
-.. _`Quickbooks`: https://quickbooks.intuit.com/
-
-Please see the `Rattail Project`_ for more information.
-
-.. _`Rattail Project`: https://rattailproject.org/
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..c3a0bb4
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,41 @@
+
+[build-system]
+requires = ["hatchling"]
+build-backend = "hatchling.build"
+
+
+[project]
+name = "tailbone-quickbooks"
+version = "0.2.0"
+description = "Tailbone integration package for Quickbooks"
+readme = "README.md"
+authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
+license = {text = "GNU GPL v3+"}
+classifiers = [
+ "Development Status :: 3 - Alpha",
+ "Environment :: Web Environment",
+ "Intended Audience :: Developers",
+ "License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
+ "Natural Language :: English",
+ "Operating System :: OS Independent",
+ "Programming Language :: Python",
+ "Programming Language :: Python :: 3",
+ "Topic :: Office/Business",
+ "Topic :: Software Development :: Libraries :: Python Modules",
+]
+dependencies = [
+ "rattail-quickbooks",
+ "Tailbone",
+]
+
+
+[project.urls]
+Homepage = "https://rattailproject.org"
+Repository = "https://kallithea.rattailproject.org/rattail-project/tailbone-quickbooks"
+Changelog = "https://kallithea.rattailproject.org/rattail-project/tailbone-quickbooks/files/master/CHANGELOG.md"
+
+
+[tool.commitizen]
+version_provider = "pep621"
+tag_format = "v$version"
+update_changelog_on_bump = true
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 0ed94fe..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- coding: utf-8; -*-
-
-[metadata]
-name = tailbone-quickbooks
-version = attr: tailbone_quickbooks.__version__
-author = Lance Edgar
-author_email = lance@edbob.org
-url = https://rattailproject.org/
-description = Tailbone integration package for Quickbooks
-long_description = file: README.rst
-classifiers =
- Development Status :: 3 - Alpha
- Environment :: Web Environment
- Intended Audience :: Developers
- License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
- Natural Language :: English
- Operating System :: OS Independent
- Programming Language :: Python
- Programming Language :: Python :: 3
- Topic :: Office/Business
- Topic :: Software Development :: Libraries :: Python Modules
-
-
-[options]
-install_requires =
- rattail-quickbooks
- Tailbone
-
-packages = find:
-include_package_data = True
diff --git a/setup.py b/setup.py
deleted file mode 100644
index cf9b79d..0000000
--- a/setup.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# -*- coding: utf-8; -*-
-################################################################################
-#
-# Rattail -- Retail Software Framework
-# Copyright © 2010-2023 Lance Edgar
-#
-# This file is part of Rattail.
-#
-# Rattail is free software: you can redistribute it and/or modify it under the
-# terms of the GNU General Public License as published by the Free Software
-# Foundation, either version 3 of the License, or (at your option) any later
-# version.
-#
-# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
-# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
-# details.
-#
-# You should have received a copy of the GNU General Public License along with
-# Rattail. If not, see .
-#
-################################################################################
-"""
-tailbone-quickbooks setup script
-"""
-
-from setuptools import setup
-
-setup()
diff --git a/tailbone_quickbooks/_version.py b/tailbone_quickbooks/_version.py
index 955f1d8..4905e5d 100644
--- a/tailbone_quickbooks/_version.py
+++ b/tailbone_quickbooks/_version.py
@@ -1,3 +1,9 @@
# -*- coding: utf-8; -*-
-__version__ = '0.1.6'
+try:
+ from importlib.metadata import version
+except ImportError:
+ from importlib_metadata import version
+
+
+__version__ = version('tailbone-quickbooks')
diff --git a/tailbone_quickbooks/templates/quickbooks/exportable-invoices/index.mako b/tailbone_quickbooks/templates/quickbooks/exportable-invoices/index.mako
index dc6ec4f..0469f32 100644
--- a/tailbone_quickbooks/templates/quickbooks/exportable-invoices/index.mako
+++ b/tailbone_quickbooks/templates/quickbooks/exportable-invoices/index.mako
@@ -97,7 +97,7 @@
this.toggleRows(uuids, checked)
}
- TailboneGrid.methods.allChecked = function(checkedList) {
+ ${grid.vue_component}.methods.allChecked = function(checkedList) {
// (de-)select all visible invoices when header checkbox is clicked
let checked = !!checkedList.length
this.toggleRows(this.allRowUUIDs(), checked)
diff --git a/tailbone_quickbooks/templates/vendors/quickbooks_bank_accounts_js.mako b/tailbone_quickbooks/templates/vendors/quickbooks_bank_accounts_js.mako
new file mode 100644
index 0000000..109bb9e
--- /dev/null
+++ b/tailbone_quickbooks/templates/vendors/quickbooks_bank_accounts_js.mako
@@ -0,0 +1,86 @@
+## -*- coding: utf-8; -*-
+
+
diff --git a/tailbone_quickbooks/templates/vendors/quickbooks_bank_accounts_widget.mako b/tailbone_quickbooks/templates/vendors/quickbooks_bank_accounts_widget.mako
new file mode 100644
index 0000000..32a6dbe
--- /dev/null
+++ b/tailbone_quickbooks/templates/vendors/quickbooks_bank_accounts_widget.mako
@@ -0,0 +1,58 @@
+## -*- coding: utf-8; -*-
+
+
+
+
+ Add Account
+
+
+
+${grid.render_table_element(data_prop='quickbooksBankAccountsData')|n}
+
+
+
+
+
+ Quickbooks Bank Account
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tailbone_quickbooks/views/vendors.py b/tailbone_quickbooks/views/vendors.py
index 7c3e70b..0ce3230 100644
--- a/tailbone_quickbooks/views/vendors.py
+++ b/tailbone_quickbooks/views/vendors.py
@@ -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,139 @@ 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(request=self.request))
+
+ # 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(self.request)
+ 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(request):
+ g = grids.Grid(request,
+ key='quickbooks_bank_accounts',
+ data=[], # 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(self.request)
+
+ g.actions.append(
+ grids.GridAction(self.request, 'edit', icon='edit',
+ click_handler='quickbooksBankAccountEdit(props.row)'))
+
+ g.actions.append(
+ grids.GridAction(self.request, '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)
diff --git a/tasks.py b/tasks.py
index 9dbdd44..2a3f1e0 100644
--- a/tasks.py
+++ b/tasks.py
@@ -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,24 +24,18 @@
Tasks for tailbone-quickbooks
"""
-import os
-
from invoke import task
-here = os.path.abspath(os.path.dirname(__file__))
-exec(open(os.path.join(here, 'tailbone_quickbooks', '_version.py')).read())
-
-
@task
def release(c):
"""
Release a new version of tailbone-quickbooks
"""
- # rebuild local tar.gz file for distribution
+ # rebuild package
+ c.run('rm -rf dist')
c.run('rm -rf tailbone_quickbooks.egg-info')
- c.run('python setup.py sdist --formats=gztar')
+ c.run('python -m build --sdist')
# upload to PyPI
- filename = 'tailbone-quickbooks-{}.tar.gz'.format(__version__)
- c.run('twine upload dist/{}'.format(filename))
+ c.run('twine upload dist/*')