Compare commits

...

25 commits

Author SHA1 Message Date
Lance Edgar 7249598b35 bump: version 0.3.1 → 0.3.2 2024-11-18 19:17:09 -06:00
Lance Edgar 3514b47776 fix: add startup workaround for CORE model imports, to avoid error
still not 100% clear why this has started happening but clearly has
something to do with changes to wuttjamaican / rattail config startup
sequence..
2024-11-18 19:15:51 -06:00
Lance Edgar 494cae2433 docs: update project links, kallithea -> forgejo 2024-09-14 13:39:25 -05:00
Lance Edgar 06d1ada3fd docs: use markdown for readme file 2024-09-13 18:13:45 -05:00
Lance Edgar 827927355d bump: version 0.3.0 → 0.3.1 2024-08-18 20:09:01 -05:00
Lance Edgar 6a26de3622 fix: refactory grid usage per wuttaweb 2024-08-16 15:05:03 -05:00
Lance Edgar 5e60a9e0af bump: version 0.2.1 → 0.3.0 2024-08-06 23:23:06 -05:00
Lance Edgar 1c5e3296d7 add view for CORE Member Contact Preferences 2024-08-06 11:33:01 -05:00
Lance Edgar 25d1eaa816 feat: add view for CORE Custom Receipt Lines (op.customReceipt) 2024-08-06 10:39:33 -05:00
Lance Edgar a7ff4e4ffc bump: version 0.2.0 → 0.2.1 2024-07-05 00:01:30 -05:00
Lance Edgar 4a3f4cf829 fix: add link to CORE for profile employee tab 2024-07-04 18:29:35 -05:00
Lance Edgar af7deee26b fix: link to CORE when viewing profile member tab 2024-07-02 14:21:28 -05:00
Lance Edgar 4bb35b50f4 fix: remove dependency for six package 2024-07-01 16:43:02 -05:00
Lance Edgar eae58f97ad build: fix command to build package, per newer convention 2024-06-10 19:05:31 -05:00
Lance Edgar 5b4d4729eb bump: version 0.1.39 → 0.2.0 2024-06-10 19:04:22 -05:00
Lance Edgar 1c39dbd881 feat: switch from setup.cfg to pyproject.toml + hatchling 2024-06-10 19:04:06 -05:00
Lance Edgar 9d1ae0a601 Update changelog 2024-06-03 10:30:04 -05:00
Lance Edgar 01afff93bf Use standard xref buttons for CORE record views 2024-05-31 14:46:19 -05:00
Lance Edgar 3c28c80074 Update changelog 2024-05-30 09:28:32 -05:00
Lance Edgar bf13401687 Misc. tweaks per latest convention etc. 2024-05-29 21:59:23 -05:00
Lance Edgar 08887a9053 Fix default dist filename for release task
not sure why this fix was needed, did setuptools behavior change?
2024-04-25 14:09:30 -05:00
Lance Edgar 0277264b2e Update changelog 2024-04-25 14:07:37 -05:00
Lance Edgar 6dc26fd4be Allow deleting rows from equity import batch after execution
in some cases we need to delete proper equity payment records, but
that can't happen if referenced by an import batch.  so admin needs
the ability to delete referencing rows for sake of deleting payment
2024-04-25 14:05:56 -05:00
Lance Edgar c945b887d6 Update changelog 2023-11-30 18:26:37 -06:00
Lance Edgar 95cdea2aef Show card number filter by default, for CORE Stock Purchases grid 2023-11-30 18:25:49 -06:00
25 changed files with 294 additions and 108 deletions

2
.gitignore vendored
View file

@ -1,2 +1,4 @@
*~
*.pyc
dist/
tailbone_corepos.egg-info/

View file

@ -5,6 +5,54 @@ All notable changes to tailbone-corepos 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.3.2 (2024-11-18)
### Fix
- add startup workaround for CORE model imports, to avoid error
## v0.3.1 (2024-08-18)
### Fix
- refactory grid usage per wuttaweb
## v0.3.0 (2024-08-06)
### Feat
- add view for CORE Custom Receipt Lines (`op.customReceipt`)
## v0.2.1 (2024-07-05)
### Fix
- add link to CORE for profile employee tab
- link to CORE when viewing profile member tab
- remove dependency for `six` package
## v0.2.0 (2024-06-10)
### Feat
- switch from setup.cfg to pyproject.toml + hatchling
## [0.1.39] - 2024-06-03
### Changed
- Use standard xref buttons for CORE record views.
## [0.1.38] - 2024-05-30
### Changed
- Misc. tweaks per latest convention etc.
## [0.1.37] - 2024-04-25
### Changed
- Allow deleting rows from equity import batch after execution.
## [0.1.36] - 2023-11-30
### Changed
- Show card number filter by default, for CORE Stock Purchases grid.
## [0.1.35] - 2023-11-05
### Changed
- Fix session used for downloading CORE archive transactions.

12
README.md Normal file
View file

@ -0,0 +1,12 @@
# tailbone-corepos
Rattail is a retail software framework, released under the GNU General
Public License.
This package contains software interfaces for the [CORE
POS](https://www.core-pos.com/) system, which is open source and
freely available.
Please see Rattail's [home page](https://rattailproject.org/) for more
information.

View file

@ -1,15 +0,0 @@
tailbone-corepos
================
Rattail is a retail software framework, released under the GNU General Public
License.
This package contains software interfaces for the `CORE POS`_ system, which is
open source and freely available.
.. _`CORE POS`: https://www.core-pos.com/
Please see Rattail's `home page`_ for more information.
.. _`home page`: https://rattailproject.org/

49
pyproject.toml Normal file
View file

@ -0,0 +1,49 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "tailbone-corepos"
version = "0.3.2"
description = "Tailbone interfaces for CORE POS"
readme = "README.md"
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
license = {text = "GNU GPL v3+"}
classifiers = [
"Development Status :: 4 - Beta",
"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",
"Programming Language :: Python :: 3.5",
"Topic :: Office/Business",
"Topic :: Software Development :: Libraries :: Python Modules",
]
dependencies = [
"rattail",
"rattail-corepos",
"Tailbone",
]
[project.urls]
Homepage = "https://rattailproject.org"
Repository = "https://forgejo.wuttaproject.org/rattail/tailbone-corepos"
Issues = "https://forgejo.wuttaproject.org/rattail/tailbone-corepos/issues"
Changelog = "https://forgejo.wuttaproject.org/rattail/tailbone-corepos/src/branch/master/CHANGELOG.md"
[project.entry-points."tailbone.providers"]
tailbone_corepos = "tailbone_corepos.provider:TailboneCorePosProvider"
[tool.commitizen]
version_provider = "pep621"
tag_format = "v$version"
update_changelog_on_bump = true

View file

@ -1,41 +0,0 @@
# -*- coding: utf-8; -*-
[metadata]
name = tailbone-corepos
version = attr: tailbone_corepos.__version__
author = Lance Edgar
author_email = lance@edbob.org
url = https://rattailproject.org/
license = GNU GPL v3
description = Tailbone interfaces for CORE POS
long_description = file: README.rst
classifiers =
Development Status :: 4 - Beta
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
Programming Language :: Python :: 3.5
Topic :: Office/Business
Topic :: Software Development :: Libraries :: Python Modules
[options]
install_requires =
rattail
rattail-corepos
six
Tailbone
packages = find:
include_package_data = True
zip_safe = False
[options.entry_points]
tailbone.providers =
tailbone_corepos = tailbone_corepos.provider:TailboneCorePosProvider

View file

@ -1,3 +1,6 @@
# -*- coding: utf-8; -*-
__version__ = '0.1.35'
from importlib.metadata import version
__version__ = version('tailbone-corepos')

View file

@ -71,6 +71,11 @@ def make_corepos_menu(request):
'route': 'corepos.member_types',
'perm': 'corepos.member_types.list',
},
{
'title': "Member Contact Preferences",
'route': 'corepos.member_contact_prefs',
'perm': 'corepos.member_contact_prefs.list',
},
{
'title': "Employees",
'route': 'corepos.employees',
@ -202,6 +207,11 @@ def make_corepos_menu(request):
'route': 'corepos.tenders',
'perm': 'corepos.tenders.list',
},
{
'title': "Custom Receipt Lines",
'route': 'corepos.custom_receipt_lines',
'perm': 'corepos.custom_receipt_lines.list',
},
],
},
{

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
@ -24,6 +24,7 @@
Tailbone Provider for CORE-POS Integration
"""
import sqlalchemy as sa
from sqlalchemy.orm import sessionmaker, scoped_session
from zope.sqlalchemy import register
@ -71,6 +72,37 @@ class TailboneCorePosProvider(TailboneProvider):
register(Session)
ExtraCoreTransArchiveSessions[key] = Session
# must import all sqlalchemy models before things get rolling,
# otherwise can have errors about continuum TransactionMeta class
# not yet mapped, when relevant pages are first requested...
# cf. https://docs.pylonsproject.org/projects/pyramid_cookbook/en/latest/database/sqlalchemy.html#importing-all-sqlalchemy-models
# hat tip to https://stackoverflow.com/a/59241485
if rattail_config.core_office_op_engine:
app = rattail_config.get_app()
corepos = app.get_corepos_handler()
# nb. use fake db to avoid true cxn errors, since the only
# point of this is to load the models
engine = sa.create_engine('sqlite://')
# office_op
core_model = corepos.get_model_office_op()
core_session = corepos.make_session_office_op(bind=engine)
try:
core_session.query(core_model.Store).first()
except sa.exc.OperationalError:
pass
core_session.close()
# office_trans
core_model = corepos.get_model_office_trans()
core_session = corepos.make_session_office_trans(bind=engine)
try:
core_session.query(core_model.TransactionDetail).first()
except sa.exc.OperationalError:
pass
core_session.close()
def get_provided_views(self):
return {

View file

@ -1,3 +0,0 @@
## -*- coding: utf-8; -*-
<%inherit file="/core-pos/master/view.mako" />
${parent.body()}

View file

@ -1,3 +0,0 @@
## -*- coding: utf-8; -*-
<%inherit file="/core-pos/master/view.mako" />
${parent.body()}

View file

@ -1,3 +0,0 @@
## -*- coding: utf-8; -*-
<%inherit file="/core-pos/master/view.mako" />
${parent.body()}

View file

@ -1,11 +0,0 @@
## -*- coding: utf-8; -*-
<%inherit file="/master/view.mako" />
<%namespace file="/corepos-util.mako" import="render_xref_helper" />
<%def name="object_helpers()">
${parent.object_helpers()}
${render_xref_helper()}
</%def>
${parent.body()}

View file

@ -1,3 +0,0 @@
## -*- coding: utf-8; -*-
<%inherit file="/core-pos/master/view.mako" />
${parent.body()}

View file

@ -1,3 +0,0 @@
## -*- coding: utf-8; -*-
<%inherit file="/core-pos/master/view.mako" />
${parent.body()}

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
@ -45,6 +45,7 @@ class CoreEquityImportBatchView(BatchMasterView):
route_prefix = 'batch.corepos.equity_import'
url_prefix = '/batch/corepos-equity-import'
rows_bulk_deletable = True
rows_deletable_if_executed = True
row_labels = {
'member_type_id': "Member Type",

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
@ -48,6 +48,7 @@ def defaults(config, **kwargs):
config.include(mod('tailbone_corepos.views.corepos.customers'))
config.include(mod('tailbone_corepos.views.corepos.employees'))
config.include(mod('tailbone_corepos.views.corepos.coupons'))
config.include(mod('tailbone_corepos.views.corepos.receipts'))
config.include(mod('tailbone_corepos.views.corepos.tenders'))
config.include(mod('tailbone_corepos.views.corepos.stockpurchases'))
config.include(mod('tailbone_corepos.views.corepos.taxrates'))

View file

@ -125,7 +125,8 @@ class UserGroupView(CoreOfficeMasterView):
permission_prefix = self.get_permission_prefix()
factory = self.get_grid_factory()
g = factory(
key='{}.users'.format(route_prefix),
self.request,
key=f'{route_prefix}.users',
data=[],
columns=[
'username',

View file

@ -137,6 +137,19 @@ class CoreMasterView(MasterView):
return kwargs
def get_xref_buttons(self, obj):
buttons = super().get_xref_buttons(obj)
app = self.get_rattail_app()
corepos = app.get_corepos_handler()
office_url = corepos.get_office_url()
if office_url:
url = self.core_office_object_url(office_url, obj)
if url:
buttons.append(self.make_xref_button(text="View in CORE Office", url=url))
return buttons
def core_office_object_url(self, office_url, obj):
"""
Subclass must define this logic; should return the "final" CORE Office

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
@ -26,8 +26,8 @@ CORE-POS member views
from sqlalchemy import orm
from corepos.db.office_op import model as corepos
from corepos.db.office_trans import model as coretrans
from corepos.db.office_op.model import MemberType, MemberContactPreference, MemberInfo
from corepos.db.office_trans.model import StockPurchase
from webhelpers2.html import HTML, tags
@ -39,7 +39,7 @@ class MemberTypeView(CoreOfficeMasterView):
"""
Master view for member types
"""
model_class = corepos.MemberType
model_class = MemberType
model_title = "CORE-POS Member Type"
url_prefix = '/core-pos/member-types'
route_prefix = 'corepos.member_types'
@ -56,11 +56,29 @@ class MemberTypeView(CoreOfficeMasterView):
g.set_link('description')
class MemberContactPreferenceView(CoreOfficeMasterView):
"""
Master view for member contact preferences
"""
model_class = MemberContactPreference
model_title = "CORE-POS Member Contact Preference"
url_prefix = '/core-pos/member-contact-prefs'
route_prefix = 'corepos.member_contact_prefs'
def configure_grid(self, g):
super().configure_grid(g)
g.set_sort_defaults('id')
g.set_link('id')
g.set_link('description')
class MemberView(CoreOfficeMasterView):
"""
Master view for members
"""
model_class = corepos.MemberInfo
model_class = MemberInfo
model_title = "CORE-POS Member (classic)"
model_title_plural = "CORE-POS Members (classic)"
url_prefix = '/core-pos/members'
@ -107,7 +125,7 @@ class MemberView(CoreOfficeMasterView):
]
has_rows = True
model_row_class = coretrans.StockPurchase
model_row_class = StockPurchase
rows_title = "Stock Purchases"
row_labels = {
@ -196,6 +214,9 @@ class MemberView(CoreOfficeMasterView):
return tags.link_to(text, url)
def render_equity_live_balance(self, member, field):
app = self.get_rattail_app()
corepos = app.get_corepos_handler()
coretrans = corepos.get_model_office_trans()
try:
balance = CoreTransSession.query(coretrans.EquityLiveBalance)\
.filter(coretrans.EquityLiveBalance.member_number == member.card_number)\
@ -214,11 +235,14 @@ class MemberView(CoreOfficeMasterView):
return [self.make_xref_button(url=url, text="View in CORE Office")]
def get_row_data(self, member):
app = self.get_rattail_app()
corepos = app.get_corepos_handler()
coretrans = corepos.get_model_office_trans()
return CoreTransSession.query(coretrans.StockPurchase)\
.filter(coretrans.StockPurchase.card_number == member.card_number)
def get_parent(self, stock_purchase):
return self.Session.get(corepos.MemberInfo, stock_purchase.card_number)
return self.Session.get(MemberInfo, stock_purchase.card_number)
def configure_row_grid(self, g):
super().configure_row_grid(g)
@ -241,6 +265,9 @@ def defaults(config, **kwargs):
MemberTypeView = kwargs.get('MemberTypeView', base['MemberTypeView'])
MemberTypeView.defaults(config)
MemberContactPreferenceView = kwargs.get('MemberContactPreferenceView', base['MemberContactPreferenceView'])
MemberContactPreferenceView.defaults(config)
MemberView = kwargs.get('MemberView', base['MemberView'])
MemberView.defaults(config)

View file

@ -44,6 +44,7 @@ class ProductView(CoreOfficeMasterView):
labels = {
'id': "ID",
'store_id': "Store ID",
'upc': "UPC",
'department_number': "Dept. No.",
'foodstamp': "Food Stamp",
@ -247,7 +248,8 @@ class ProductView(CoreOfficeMasterView):
factory = self.get_grid_factory()
g = factory(
key='{}.vendor_items'.format(route_prefix),
self.request,
key=f'{route_prefix}.vendor_items',
data=[],
columns=[
'vendor_name',
@ -263,7 +265,7 @@ class ProductView(CoreOfficeMasterView):
},
)
return HTML.literal(
g.render_buefy_table_element(data_prop='vendorItemsData'))
g.render_table_element(data_prop='vendorItemsData'))
def template_kwargs_view(self, **kwargs):
kwargs = super().template_kwargs_view(**kwargs)

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
@ -20,7 +20,31 @@
# Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
CORE POS receipt views
"""
from setuptools import setup
from corepos.db.office_op.model import CustomReceiptLine
setup()
from .master import CoreOfficeMasterView
class CustomReceiptLineView(CoreOfficeMasterView):
"""
Master view for custom receipt text
"""
model_class = CustomReceiptLine
model_title = "CORE-POS Custom Receipt Line"
route_prefix = 'corepos.custom_receipt_lines'
url_prefix = '/core-pos/custom-receipt-lines'
def defaults(config, **kwargs):
base = globals()
CustomReceiptLineView = kwargs.get('CustomReceiptLineView', base['CustomReceiptLineView'])
CustomReceiptLineView.defaults(config)
def includeme(config):
defaults(config)

View file

@ -44,13 +44,18 @@ class StockPurchaseView(CoreTransMasterView):
}
def configure_grid(self, g):
""" """
super().configure_grid(g)
# card_number
g.filters['card_number'].default_active = True
g.filters['card_number'].default_verb = 'equal'
g.set_link('card_number')
g.set_type('amount', 'currency')
g.set_sort_defaults('datetime', 'desc')
g.set_link('card_number')
g.set_link('transaction_number')
g.set_link('datetime')
@ -61,6 +66,7 @@ class StockPurchaseView(CoreTransMasterView):
return {'totals_display': app.render_currency(total)}
def configure_form(self, f):
""" """
super().configure_form(f)
f.set_renderer('card_number', self.render_linked_corepos_card_number)

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2023 Lance Edgar
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
@ -50,6 +50,7 @@ class PersonViewSupplement(ViewSupplement):
def configure_form(self, f):
if not self.master.creating:
f.append('corepos_customer_id')
f.set_required('corepos_customer_id', False)
def get_version_child_classes(self):
model = self.model
@ -67,6 +68,30 @@ class PersonViewSupplement(ViewSupplement):
return context
def get_context_for_member(self, member, context):
if member.corepos_card_number:
app = self.get_rattail_app()
corepos = app.get_corepos_handler()
url = corepos.get_office_member_url(member.corepos_card_number)
if url:
context['external_links'].append({'label': "View in CORE Office",
'url': url})
return context
def get_context_for_employee(self, employee, context):
if employee.corepos_number:
app = self.get_rattail_app()
corepos = app.get_corepos_handler()
url = corepos.get_office_employee_url(employee.corepos_number)
if url:
context['external_links'].append({'label': "View in CORE Office",
'url': url})
return context
def get_member_xref_buttons(self, person):
buttons = OrderedDict()
app = self.get_rattail_app()

View file

@ -2,7 +2,7 @@
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2019 Lance Edgar
# Copyright © 2010-2024 Lance Edgar
#
# This file is part of Rattail.
#
@ -25,21 +25,33 @@ Tasks for tailbone-corepos
"""
import os
import re
import shutil
from invoke import task
here = os.path.abspath(os.path.dirname(__file__))
exec(open(os.path.join(here, 'tailbone_corepos', '_version.py')).read())
__version__ = None
pattern = re.compile(r'^version = "(\d+\.\d+\.\d+)"$')
with open(os.path.join(here, 'pyproject.toml'), 'rt') as f:
for line in f:
line = line.rstrip('\n')
match = pattern.match(line)
if match:
__version__ = match.group(1)
break
if not __version__:
raise RuntimeError("could not parse version!")
@task
def release(ctx):
def release(c):
"""
Release a new version of 'tailbone-corepos'.
"""
shutil.rmtree('tailbone_corepos.egg-info')
ctx.run('python setup.py sdist --formats=gztar')
filename = 'tailbone-corepos-{}.tar.gz'.format(__version__)
ctx.run('twine upload dist/{}'.format(filename))
if os.path.exists('tailbone_corepos.egg-info'):
shutil.rmtree('tailbone_corepos.egg-info')
c.run('python -m build --sdist')
filename = f'tailbone_corepos-{__version__}.tar.gz'
c.run('twine upload dist/{}'.format(filename))