Compare commits

..

No commits in common. "master" and "v0.1.35" have entirely different histories.

25 changed files with 108 additions and 294 deletions

2
.gitignore vendored
View file

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

View file

@ -5,54 +5,6 @@ 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/) 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). 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 ## [0.1.35] - 2023-11-05
### Changed ### Changed
- Fix session used for downloading CORE archive transactions. - Fix session used for downloading CORE archive transactions.

View file

@ -1,12 +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](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.

15
README.rst Normal file
View file

@ -0,0 +1,15 @@
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/

View file

@ -1,49 +0,0 @@
[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

41
setup.cfg Normal file
View file

@ -0,0 +1,41 @@
# -*- 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

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar # Copyright © 2010-2023 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -20,31 +20,7 @@
# Rattail. If not, see <http://www.gnu.org/licenses/>. # Rattail. If not, see <http://www.gnu.org/licenses/>.
# #
################################################################################ ################################################################################
"""
CORE POS receipt views
"""
from corepos.db.office_op.model import CustomReceiptLine from setuptools import setup
from .master import CoreOfficeMasterView setup()
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

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

View file

@ -71,11 +71,6 @@ def make_corepos_menu(request):
'route': 'corepos.member_types', 'route': 'corepos.member_types',
'perm': 'corepos.member_types.list', 'perm': 'corepos.member_types.list',
}, },
{
'title': "Member Contact Preferences",
'route': 'corepos.member_contact_prefs',
'perm': 'corepos.member_contact_prefs.list',
},
{ {
'title': "Employees", 'title': "Employees",
'route': 'corepos.employees', 'route': 'corepos.employees',
@ -207,11 +202,6 @@ def make_corepos_menu(request):
'route': 'corepos.tenders', 'route': 'corepos.tenders',
'perm': 'corepos.tenders.list', '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 # Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar # Copyright © 2010-2023 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -24,7 +24,6 @@
Tailbone Provider for CORE-POS Integration Tailbone Provider for CORE-POS Integration
""" """
import sqlalchemy as sa
from sqlalchemy.orm import sessionmaker, scoped_session from sqlalchemy.orm import sessionmaker, scoped_session
from zope.sqlalchemy import register from zope.sqlalchemy import register
@ -72,37 +71,6 @@ class TailboneCorePosProvider(TailboneProvider):
register(Session) register(Session)
ExtraCoreTransArchiveSessions[key] = 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): def get_provided_views(self):
return { return {

View file

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

View file

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

View file

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

View file

@ -0,0 +1,11 @@
## -*- 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

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

View file

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

View file

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

View file

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

View file

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

View file

@ -137,19 +137,6 @@ class CoreMasterView(MasterView):
return kwargs 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): def core_office_object_url(self, office_url, obj):
""" """
Subclass must define this logic; should return the "final" CORE Office Subclass must define this logic; should return the "final" CORE Office

View file

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

View file

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

View file

@ -44,18 +44,13 @@ class StockPurchaseView(CoreTransMasterView):
} }
def configure_grid(self, g): def configure_grid(self, g):
""" """
super().configure_grid(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_type('amount', 'currency')
g.set_sort_defaults('datetime', 'desc') g.set_sort_defaults('datetime', 'desc')
g.set_link('card_number')
g.set_link('transaction_number') g.set_link('transaction_number')
g.set_link('datetime') g.set_link('datetime')
@ -66,7 +61,6 @@ class StockPurchaseView(CoreTransMasterView):
return {'totals_display': app.render_currency(total)} return {'totals_display': app.render_currency(total)}
def configure_form(self, f): def configure_form(self, f):
""" """
super().configure_form(f) super().configure_form(f)
f.set_renderer('card_number', self.render_linked_corepos_card_number) f.set_renderer('card_number', self.render_linked_corepos_card_number)

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar # Copyright © 2010-2023 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -50,7 +50,6 @@ class PersonViewSupplement(ViewSupplement):
def configure_form(self, f): def configure_form(self, f):
if not self.master.creating: if not self.master.creating:
f.append('corepos_customer_id') f.append('corepos_customer_id')
f.set_required('corepos_customer_id', False)
def get_version_child_classes(self): def get_version_child_classes(self):
model = self.model model = self.model
@ -68,30 +67,6 @@ class PersonViewSupplement(ViewSupplement):
return context 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): def get_member_xref_buttons(self, person):
buttons = OrderedDict() buttons = OrderedDict()
app = self.get_rattail_app() app = self.get_rattail_app()

View file

@ -2,7 +2,7 @@
################################################################################ ################################################################################
# #
# Rattail -- Retail Software Framework # Rattail -- Retail Software Framework
# Copyright © 2010-2024 Lance Edgar # Copyright © 2010-2019 Lance Edgar
# #
# This file is part of Rattail. # This file is part of Rattail.
# #
@ -25,33 +25,21 @@ Tasks for tailbone-corepos
""" """
import os import os
import re
import shutil import shutil
from invoke import task from invoke import task
here = os.path.abspath(os.path.dirname(__file__)) here = os.path.abspath(os.path.dirname(__file__))
__version__ = None exec(open(os.path.join(here, 'tailbone_corepos', '_version.py')).read())
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 @task
def release(c): def release(ctx):
""" """
Release a new version of 'tailbone-corepos'. Release a new version of 'tailbone-corepos'.
""" """
if os.path.exists('tailbone_corepos.egg-info'): shutil.rmtree('tailbone_corepos.egg-info')
shutil.rmtree('tailbone_corepos.egg-info') ctx.run('python setup.py sdist --formats=gztar')
c.run('python -m build --sdist') filename = 'tailbone-corepos-{}.tar.gz'.format(__version__)
filename = f'tailbone_corepos-{__version__}.tar.gz' ctx.run('twine upload dist/{}'.format(filename))
c.run('twine upload dist/{}'.format(filename))