Compare commits

...

136 commits

Author SHA1 Message Date
feb2d3471e bump: version 0.5.0 → 0.5.1 2025-02-20 09:31:29 -06:00
9b9260ba4b fix: add Product.default_vendor_item convenience property 2025-02-20 09:30:24 -06:00
8359e5692e bump: version 0.4.0 → 0.5.0 2025-02-01 15:19:12 -06:00
97a1396a54 feat: use true column names for transaction data models
as much as i kind of want to "rename" some of these for convenience,
it seems safest here to just stick with true names to avoid confusion
2025-01-25 17:04:43 -06:00
a2a1d7faee fix: define common base schema for Product model 2025-01-25 17:04:40 -06:00
2fe089bd57 fix: add Parameter model for lane_op 2025-01-25 16:41:17 -06:00
50351596ac fix: add model for lane_trans LocalTrans 2025-01-24 20:18:13 -06:00
28cb23adc4 bump: version 0.3.5 → 0.4.0 2025-01-24 19:59:18 -06:00
c3b639390d fix: add Employee model for lane_op
with abstract common base schema
2025-01-24 19:21:00 -06:00
97fb0b28cb feat: add common base class for dtransactions and similar models
more work to be done here i'm sure, but this should hopefully be a
conservative change in the right direction
2025-01-24 19:20:18 -06:00
d21346bbff fix: fix ordering of name columns for MemberInfo
so they show up correctly by default e.g. in UI
2025-01-15 14:48:18 -06:00
6c1fc9a803 bump: version 0.3.4 → 0.3.5 2025-01-15 11:00:22 -06:00
7aaa35dac7 fix: add workaround to avoid missing schema columns
more columns will need to be added to this workaround i'm sure, but
this takes care of a couple small ones
2025-01-15 10:59:14 -06:00
b8ca60b508 bump: version 0.3.3 → 0.3.4 2025-01-15 08:51:40 -06:00
01852ceecc fix: misc. cleanup for sales batch models 2025-01-15 08:45:59 -06:00
ab56a35acc fix: add more enums for batch discount type, editor UI
also fix column type for editor UI
2025-01-14 20:20:41 -06:00
e37cf88cd9 bump: version 0.3.2 → 0.3.3 2025-01-13 13:31:21 -06:00
023d826d31 fix: remove autoincrement option for composite PK fields 2025-01-13 12:57:41 -06:00
465e565f7b bump: version 0.3.1 → 0.3.2 2025-01-11 22:01:38 -06:00
47c23a65ae fix: add base class for all transaction tables, views
also rename some classes; add `dlogBig` for archive view
2025-01-11 21:55:54 -06:00
464f107a88 fix: add MemberType.ignore_sales column 2025-01-11 21:52:22 -06:00
cb6ed15eb8 fix: add model for MasterSuperDepartment 2025-01-11 21:51:28 -06:00
310a261b48 bump: version 0.3.0 → 0.3.1 2024-12-17 16:45:36 -06:00
3bb01f2397 fix: add wicable, active columns for Department model 2024-12-17 16:45:11 -06:00
Lance Edgar
ed48f9134a docs: update project links to forgejo 2024-09-14 10:43:39 -05:00
Lance Edgar
b44e5a3d60 docs: use markdown for readme file 2024-09-13 18:49:14 -05:00
Lance Edgar
b425095a8d bump: version 0.2.1 → 0.3.0 2024-08-06 23:21:26 -05:00
Lance Edgar
b5b29cdcf1 feat: add model for MemberContactPreference (op.memContactPrefs) 2024-08-06 11:34:07 -05:00
Lance Edgar
27a46ed18f feat: add model for CustomReceiptLine (op.customReceipt) 2024-08-06 10:38:01 -05:00
Lance Edgar
2df3885420 bump: version 0.2.0 → 0.2.1 2024-07-04 23:59:49 -05:00
Lance Edgar
bcff560555 fix: add API methods, get_employees() and get_employee() 2024-07-04 18:27:50 -05:00
Lance Edgar
66cf108b3f fix: remove Change data model
since it isn't actually part of CORE, and is now handled in other ways
with regard to datasync
2024-07-04 13:18:45 -05:00
Lance Edgar
e9638c73a4 fix: remove dependency for six package 2024-07-01 16:32:39 -05:00
Lance Edgar
3b54dbd068 docs: use more specific project homepage url 2024-06-14 19:48:40 -05:00
Lance Edgar
40e647d5c8 build: use newer convention when building for release 2024-06-10 15:57:09 -05:00
Lance Edgar
04948fb840 bump: version 0.1.20 → 0.2.0 2024-06-10 15:55:51 -05:00
Lance Edgar
5cf8d2ac05 feat: switch from setup.cfg to pyproject.toml + hatchling 2024-06-10 15:55:14 -05:00
Lance Edgar
f80d03daaa Fix default dist filename for release task
not sure why this fix was needed, did setuptools behavior change?
2024-05-29 10:10:25 -05:00
Lance Edgar
777b198e44 Update changelog 2024-05-29 10:09:15 -05:00
Lance Edgar
df6f0d9793 Add enum for CORE (Office) DB types
for use with typer commands
2024-05-16 19:13:35 -05:00
Lance Edgar
7cd89029ac Update changelog 2023-11-01 08:15:37 -05:00
Lance Edgar
909d75796b Fix synonym for dtransactions.tax 2023-10-20 19:03:20 -05:00
Lance Edgar
f217f00a8f Fix data types for tax, voided in dtransactions 2023-10-20 14:31:13 -05:00
Lance Edgar
67dd9777ba Update changelog 2023-10-12 10:38:44 -05:00
Lance Edgar
1597c163c6 Let MemberInfo.dates be an object, not a list 2023-10-12 10:34:13 -05:00
Lance Edgar
3a3fba19e4 Fix the Department.tax_rate relationship
whoops i mistook `dept_tax` for a boolean previously
2023-10-11 18:36:12 -05:00
Lance Edgar
7171531dce Update changelog 2023-10-07 18:58:17 -05:00
Lance Edgar
e5988102ad Rename module to corepos.db.office_arch 2023-10-05 11:52:39 -05:00
Lance Edgar
f06f236a60 Update changelog 2023-09-15 12:59:52 -05:00
Lance Edgar
83eecad28d Add model for office_op.Tender 2023-09-15 12:43:02 -05:00
Lance Edgar
541adb6979 Update changelog 2023-09-13 21:51:10 -05:00
Lance Edgar
5214db0a83 Add model for CustomerNotifications table 2023-09-13 16:23:33 -05:00
Lance Edgar
0dda359094 Update changelog 2023-09-07 18:37:48 -05:00
Lance Edgar
0e30303947 Tweak primary key for StockPurchase model
per having a better understanding now, i think..
2023-09-07 17:45:14 -05:00
Lance Edgar
5921a18f12 Update changelog 2023-09-02 13:56:59 -05:00
Lance Edgar
5a77d14a26 Add models for StockPurchase and EquityLiveBalance 2023-06-18 11:38:14 -05:00
Lance Edgar
b2622c473d Update changelog 2023-06-12 20:39:12 -05:00
Lance Edgar
c6144ab310 Add note about meminfo.email_2 field, aka. "alt. phone" 2023-06-12 20:38:12 -05:00
Lance Edgar
4952d2fa3d Rename custdata model to CustomerClassic 2023-06-12 17:28:55 -05:00
Lance Edgar
852a989bd5 Add get_member_types() method for CORE API 2023-06-06 13:13:51 -05:00
Lance Edgar
bb8278dcc8 Update changelog 2023-06-02 14:26:51 -05:00
Lance Edgar
d58426c073 Add support for htdigest auth when using CORE webservices API 2023-05-22 21:34:46 -05:00
Lance Edgar
757fb50a96 Update changelog 2023-05-17 06:57:14 -05:00
Lance Edgar
6e43449ecb Replace setup.py contents with setup.cfg 2023-05-16 13:16:41 -05:00
Lance Edgar
24fb8c8fea Update changelog 2023-05-01 22:17:38 -05:00
Lance Edgar
ad5837405f Require SQLAlchemy 1.4.x 2023-02-15 12:51:50 -06:00
Lance Edgar
2444628c13 Update changelog 2023-01-02 16:56:30 -06:00
Lance Edgar
c2723de467 Delete productUser record when products record is deleted 2022-08-21 00:11:24 -05:00
Lance Edgar
2b80fd6a6b Add basic TransactionDetail for trans archive model 2022-03-26 23:04:05 -05:00
Lance Edgar
af6189b237 Update changelog 2022-03-02 21:35:57 -06:00
Lance Edgar
b46282264c Add model for UserGroup 2022-01-17 18:59:19 -06:00
Lance Edgar
a6306a4882 Remove deprecation warning for corepos.db
everything should be using the right imports now
2021-12-30 21:47:55 -06:00
Lance Edgar
ae45615717 Update changelog 2021-11-04 21:26:22 -05:00
Lance Edgar
41c142b837 Add the custdata model for lane_op DB
also use Numeric instead of Float for "MONEY" columns, for custdata in
both lane_op and office_op
2021-11-04 17:41:39 -05:00
Lance Edgar
754d8697e8 Add proper support for str(Suspension) 2021-09-30 18:44:53 -04:00
Lance Edgar
0d81a41b54 Add User model for office_op 2021-09-07 15:45:51 -05:00
Lance Edgar
69c7be6356 Update changelog 2021-08-31 22:42:22 -05:00
Lance Edgar
092884eab3 Add lane_op model for Department 2021-08-31 22:40:58 -05:00
Lance Edgar
07e3c62b6c Update changelog 2021-08-02 09:07:50 -05:00
Lance Edgar
e4c46b3fa4 Add schema for TableSyncRules 2021-08-02 08:56:05 -05:00
Lance Edgar
b4a07f9875 Update changelog 2021-07-21 20:17:56 -05:00
Lance Edgar
178acdac31 Add basic 'lane_op' DB schema
just the 'products' table so far
2021-07-21 20:02:17 -05:00
Lance Edgar
0d3d007bd1 Update changelog 2021-06-11 18:04:42 -05:00
Lance Edgar
170f0a769a Remove duplicated column name 2021-05-07 11:12:44 -05:00
Lance Edgar
d94189c404 Add FK constraint for ProductLikeCode.upc
not sure why that wasn't already present?
2021-05-04 20:10:14 -05:00
Lance Edgar
b40fbf7cab Add the Product.complete_size convenience attribute
also define `str(VendorItem)`
2021-02-15 12:59:21 -06:00
Lance Edgar
7cf4ec1295 Fetch single vendorItems record by sku instead of upc
b/c that is what we must use as PK when updating the record etc.
2021-02-09 16:11:50 -06:00
Lance Edgar
76f743f3b8 Add set_vendor_item() method for API client 2021-02-09 14:25:18 -06:00
Lance Edgar
1757d09781 Add schema model for Purchase Orders 2021-02-01 15:35:22 -06:00
Lance Edgar
ee9451588c Add basic support for Stores schema and API 2021-01-27 22:19:38 -06:00
Lance Edgar
d398e706c4 Add MemberBarcode to op model 2021-01-13 19:18:17 -06:00
Lance Edgar
19d62b535f Tweak that thing again... 2020-12-31 19:25:33 -06:00
Lance Edgar
52d9595331 Fix Department.see_id field definition
this is not a flag, but a minimum age requirement
2020-12-31 19:01:40 -06:00
Lance Edgar
ff428c4635 Misc. tweaks to product-related schema, for sake of generating SQL
e.g. from IFPS data
2020-12-09 13:09:58 -06:00
Lance Edgar
29638c062c Remove "default" values from Product model definition
i'm a bit torn about this.  on the one hand i like there being some default
values here, but realistically they didn't all make sense.  also it could be
said that default values amount to business logic and that should stay in CORE
basically.

but in the end, i needed these to go away in order to simplify generating some
SQL INSERT statements.  was using SQLAlchemy core and it kept insisting that
all fields with a "default" defined should be part of the INSERT statement, but
really i didn't want them to be.  so they no longer have defaults.
2020-12-07 17:41:01 -06:00
Lance Edgar
9dd5813520 Update changelog 2020-09-16 19:43:35 -05:00
Lance Edgar
286df47c22 Add get_vendor_items() and get_vendor_item() methods for web API 2020-09-04 19:07:41 -05:00
Lance Edgar
963f544b02 Expose some "new" columns for ScaleItem model
may need to revisit this again, when an "old" DB is encountered
2020-09-02 11:25:10 -05:00
Lance Edgar
74a28514dc Tweak relationship for Department._super_parents
since there can be more than one
2020-08-20 14:41:08 -05:00
Lance Edgar
e2dc00b469 Add model for SuperDepartment 2020-08-20 14:26:46 -05:00
Lance Edgar
472f43896b Add basic batch models 2020-08-16 16:57:54 -05:00
Lance Edgar
9299cd445f Add "origin" models for office_op schema 2020-08-10 15:57:32 -05:00
Lance Edgar
ef1a25f0dc Make sure MemberInfo.customers is sorted by person number
also improve the str() method a bit
2020-07-30 11:11:17 -05:00
Lance Edgar
4c7b208e6e Add Suspension and ReasonCodes to model 2020-07-22 20:25:20 -05:00
Lance Edgar
365d679d76 Add split_street() method for MemberInfo 2020-07-21 15:07:28 -05:00
Lance Edgar
13b8380527 Declare foreign key for CustData.member_type 2020-07-15 22:15:11 -05:00
Lance Edgar
257ed82d6d Bring __version__ into root namespace
can't remember why that wouldn't already be there..?  maybe just forgot
2020-07-15 21:42:28 -05:00
Lance Edgar
68ab8ff55d Add CustomerAccount.customer_type reference, to MemberType
not 100% sure about the best naming here, hopefully this is good
2020-07-13 11:03:32 -05:00
Lance Edgar
07291b3fa7 Add MemberNote and MemberInfo.notes to data model 2020-07-13 11:02:59 -05:00
Lance Edgar
beb73dc668 Add Product.last_sold to schema
hopefully that's a good idea..
2020-04-17 00:24:53 -05:00
Lance Edgar
4b971c289e Add LikeCode and ProductLikeCode models 2020-04-10 14:09:38 -05:00
Lance Edgar
87fd5367a1 Add table_exists() util function 2020-03-31 14:09:51 -05:00
Lance Edgar
d818100da3 Fix id field for TaxRateComponent 2020-03-31 14:09:39 -05:00
Lance Edgar
14a182c675 Disable TaxRate.sales_code attribute, for now
since some older DBs don't have it
2020-03-30 13:44:22 -05:00
Lance Edgar
64e4b18cf0 Add FloorSection and ProductPhysicalLocation models 2020-03-30 13:18:10 -05:00
Lance Edgar
acc85ba8de Add ProductUser model 2020-03-30 13:18:08 -05:00
Lance Edgar
6ecfbf4e1a Add ScaleItem model 2020-03-30 12:00:52 -05:00
Lance Edgar
5f3ad79a95 Add VendorDepartment and VendorItem models 2020-03-30 11:39:57 -05:00
Lance Edgar
9b429eb293 Revert "Try out the "synonym" approach for all Vendor model fields"
This reverts commit deea31597c.

we'll keep the synonym in place for `vendorID` but the rest of these just
caused problems, e.g. with tailbone grids
2020-03-30 11:29:21 -05:00
Lance Edgar
3a440d20bc Add enum for product price method 2020-03-30 00:05:26 -05:00
Lance Edgar
07c24b63dc Fix how we interpret Product.id_enforced values
will be an age, not a flag
2020-03-29 23:36:37 -05:00
Lance Edgar
e0058c003d Add Product.subdepartment relationship 2020-03-29 23:36:29 -05:00
Lance Edgar
9466b16b64 Add DB models for TaxRate, TaxRateComponent 2020-03-27 20:50:00 -05:00
Lance Edgar
573595497e Add get_members() API method 2020-03-18 11:28:19 -05:00
Lance Edgar
d8e93c9d2e Add set_member() API method
also rename `get_customer()` to `get_member`
2020-03-17 16:04:22 -05:00
Lance Edgar
9e850496e7 Add new Customer and CustomerAccount models 2020-03-16 16:54:42 -05:00
Lance Edgar
92f522cf65 Rename "old" Customer model, to CustData
need to make room for "new" Customer model
2020-03-16 16:54:40 -05:00
Lance Edgar
5509089741 Add get_customer() API method 2020-03-15 19:29:01 -05:00
Lance Edgar
2d0cfa30ca Add "set" API methods for Department, Subdepartment, Product 2020-03-15 15:52:28 -05:00
Lance Edgar
b76e82975a Add GET methods to API, for departments, subdepartments, products 2020-03-15 14:27:22 -05:00
Lance Edgar
deea31597c Try out the "synonym" approach for all Vendor model fields
this seems promising, but let's see if anything breaks first
2020-03-06 13:47:32 -06:00
Lance Edgar
a12c9de415 Tweak how we handle return value from API 'set' method for Vendors
also add note about using `set_vendor()` for creating new ones.

and misc. other cleanup
2020-03-04 18:54:34 -06:00
Lance Edgar
73b0acecdd Declare dependency for 'requests' lib 2020-03-03 21:49:16 -06:00
Lance Edgar
7fc5ae9b4e Add basic CORE webservices API client, for Vendor data
lots more to come yet, once the basic patterns are proven
2020-03-03 21:35:39 -06:00
Lance Edgar
a0efa1a967 Add model for datasync changes queue table
just in case it's there, for use w/ datasync watcher
2020-03-01 20:49:26 -06:00
Lance Edgar
2d61903cf7 Use twine to upload released package to PyPI 2020-02-27 21:52:05 -06:00
26 changed files with 3065 additions and 388 deletions

3
.gitignore vendored
View file

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

View file

@ -5,6 +5,177 @@ All notable changes to pyCOREPOS 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.5.1 (2025-02-20)
### Fix
- add `Product.default_vendor_item` convenience property
## v0.5.0 (2025-02-01)
### Feat
- use true column names for transaction data models
### Fix
- define common base schema for Product model
- add `Parameter` model for lane_op
- add model for lane_trans `LocalTrans`
## v0.4.0 (2025-01-24)
### Feat
- add common base class for `dtransactions` and similar models
### Fix
- add `Employee` model for lane_op
- fix ordering of name columns for MemberInfo
## v0.3.5 (2025-01-15)
### Fix
- add workaround to avoid missing schema columns
## v0.3.4 (2025-01-15)
### Fix
- misc. cleanup for sales batch models
- add more enums for batch discount type, editor UI
## v0.3.3 (2025-01-13)
### Fix
- remove `autoincrement` option for composite PK fields
## v0.3.2 (2025-01-11)
### Fix
- add base class for all transaction tables, views
- add `MemberType.ignore_sales` column
- add model for `MasterSuperDepartment`
## v0.3.1 (2024-12-17)
### Fix
- add `wicable`, `active` columns for Department model
## v0.3.0 (2024-08-06)
### Feat
- add model for `MemberContactPreference` (`op.memContactPrefs`)
- add model for `CustomReceiptLine` (`op.customReceipt`)
## v0.2.1 (2024-07-04)
### Fix
- add API methods, `get_employees()` and `get_employee()`
- remove `Change` data model
- remove dependency for `six` package
## v0.2.0 (2024-06-10)
### Feat
- switch from setup.cfg to pyproject.toml + hatchling
## [0.1.20] - 2024-05-29
### Changed
- Add enum for CORE (Office) DB types.
## [0.1.19] - 2023-11-01
### Changed
- Fix data types for tax, voided in `dtransactions`.
- Fix synonym for `dtransactions.tax`.
## [0.1.18] - 2023-10-12
### Changed
- Fix the `Department.tax_rate` relationship.
- Let `MemberInfo.dates` be an object, not a list.
## [0.1.17] - 2023-10-07
### Changed
- Rename module to `corepos.db.office_arch`.
## [0.1.16] - 2023-09-15
### Changed
- Add model for `office_op.Tender`.
## [0.1.15] - 2023-09-13
### Changed
- Add model for `CustomerNotifications` table.
## [0.1.14] - 2023-09-07
### Changed
- Tweak primary key for StockPurchase model.
## [0.1.13] - 2023-09-02
### Changed
- Add models for StockPurchase and EquityLiveBalance.
## [0.1.12] - 2023-06-12
### Changed
- Add `get_member_types()` method for CORE API.
- Rename model for `custdata` to `CustomerClassic`.
- Add note about `meminfo.email_2` field, aka. "alt. phone".
## [0.1.11] - 2023-06-02
### Changed
- Add support for htdigest auth when using CORE webservices API.
## [0.1.10] - 2023-05-17
### Changed
- Replace `setup.py` contents with `setup.cfg`.
## [0.1.9] - 2023-05-01
### Changed
- Require SQLAlchemy 1.4.x.
## [0.1.8] - 2023-01-02
### Changed
- Add basic `TransactionDetail` for trans archive model.
- Delete `productUser` record when `products` record is deleted.
## [0.1.7] - 2022-03-02
### Changed
- Remove deprecation warning for `corepos.db`.
- Add model for `UserGroup`.
## [0.1.6] - 2021-11-04
### Changed
- Add `User` model for office_op.
- Add proper support for `str(Suspension)`.
- Add the `custdata` model for lane_op DB.
## [0.1.5] - 2021-08-31
### Changed
- Add lane_op model for Department.
## [0.1.4] - 2021-08-02
### Changed
- Add schema for `TableSyncRules`.
## [0.1.3] - 2021-07-21
### Changed
- Add basic 'lane_op' DB schema.
## [0.1.2] - 2021-06-11
### Changed
- Several more updates, mostly a "save point" release.
## [0.1.1] - 2020-09-16
### Added
- A ton of updates, mostly a "save point" release.
## [0.1.0] - 2020-02-27
### Added
- Initial version of the package; defines some basic table mappings.

5
README.md Normal file
View file

@ -0,0 +1,5 @@
# pyCOREPOS
A Python interface to the [CORE POS](https://github.com/CORE-POS)
system.

View file

@ -1,7 +0,0 @@
pyCOREPOS
=========
A Python interface to the `CORE POS`_ system.
.. _CORE POS: https://github.com/CORE-POS

View file

@ -0,0 +1,27 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2020 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
CORE POS Interface
"""
from ._version import __version__

View file

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

578
corepos/api.py Normal file
View file

@ -0,0 +1,578 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2024 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
CORE-POS webservices API
"""
import json
import logging
import requests
from requests.auth import HTTPDigestAuth
log = logging.getLogger(__name__)
class CoreAPIError(Exception):
"""
Base class for errors coming from the CORE API proper.
"""
def __init__(self, message):
self.message = message
def __str__(self):
return "CORE API returned an error: {}".format(self.message)
class CoreWebAPI(object):
"""
Client implementation for the CORE webservices API.
:param str url: URL to the CORE webservices API,
e.g. ``'http://localhost/fannie/ws/'``
:param bool verify: How to handle certificate validation for HTTPS
URLs. This value is passed as-is to the ``requests`` library,
so see those docs for more info. The default value for this is
``True`` because the assumption is that security should be on
by default. Set it to ``False`` in order to disable validation
entirely, e.g. for self-signed certs. (This may also be needed
for basic HTTP URLs?) Other values may be possible also; again
see the ``requests`` docs for more info.
:param htdigest_username: Username for htdigest authentication, if
applicable.
:param htdigest_password: Password for htdigest authentication, if
applicable.
"""
def __init__(
self,
url,
verify=True,
htdigest_username=None,
htdigest_password=None,
):
self.url = url
self.verify = verify
self.session = requests.Session()
if htdigest_username and htdigest_password:
self.session.auth = HTTPDigestAuth(htdigest_username,
htdigest_password)
def post(self, params, method=None):
"""
Issue a POST request to the API, with the given ``params``. If not
specified, ``method`` will be CORE's ``FannieEntity`` webservice.
"""
if not method:
method = 'FannieEntity'
if '\\' not in method:
method = r'\COREPOS\Fannie\API\webservices\{}'.format(method)
payload = {
'jsonrpc': '2.0',
'method': method,
'params': params,
# we're not dealing with async here, so KISS for this 'id'
# https://stackoverflow.com/questions/4390369/json-rpc-how-can-one-make-a-unique-id#comment4786119_4391070
'id': 1,
}
response = self.session.post(self.url, data=json.dumps(payload),
verify=self.verify)
response.raise_for_status()
return response
def parse_response(self, response, method=None):
"""
Generic method to "parse" a response from the API. Really this just
converts the JSON to a dict (etc.), and then checks for error. If an
error is found in the response, it will be raised here.
"""
try:
js = response.json()
except:
raise CoreAPIError("Received invalid response: {}".format(response.content))
if 'error' in js:
raise CoreAPIError(js['error'])
# note, the result data format may depend on the API method involved
if method == 'FannieMember':
return js['result']
# assuming typical FannieEntity result here
assert set(js.keys()) == set(['jsonrpc', 'id', 'result'])
assert set(js['result'].keys()) == set(['result'])
return js['result']['result']
def get_member_types(self):
"""
Fetch all Member Type records from CORE.
:returns: A (potentially empty) list of member type dict records.
"""
params = {
'entity': 'Memtype',
'submethod': 'get',
'columns': {},
}
response = self.post(params)
result = self.parse_response(response)
return [json.loads(rec) for rec in result]
def get_members(self):
"""
Fetch all Member records from CORE.
:returns: A (potentially empty) list of member dict records.
"""
params = {
'method': 'get',
'cardNo': None,
}
response = self.post(params, method='FannieMember')
result = self.parse_response(response, method='FannieMember')
return result
def get_member(self, cardNo):
"""
Fetch an existing Member record from CORE.
:returns: Either a member dict record, or ``None``.
"""
params = {
'cardNo': cardNo,
'method': 'get',
}
response = self.post(params, method='FannieMember')
result = self.parse_response(response, method='FannieMember')
if result:
return result
def set_member(self, cardNo, **kwargs):
"""
Update an existing Member record in CORE.
:returns: Boolean indicating success of the operation.
.. warning::
Only simple updates have been attempted thus far; have yet to try
creation or deletion. Neither of those should be expected to work.
"""
kwargs['cardNo'] = cardNo
params = {
'cardNo': cardNo,
'method': 'set',
'member': kwargs,
}
response = self.post(params, method='FannieMember')
result = self.parse_response(response, method='FannieMember')
if result:
return result
def get_employees(self, **columns):
"""
Fetch some or all of Employee records from CORE.
:returns: A (potentially empty) list of employee dict records.
"""
params = {
'entity': 'Employees',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return [json.loads(rec) for rec in result]
def get_employee(self, emp_no, **columns):
"""
Fetch an existing Employee record from CORE.
:returns: Either a employee dict record, or ``None``.
"""
columns['emp_no'] = emp_no
params = {
'entity': 'Employees',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
if result:
if len(result) > 1:
log.warning("CORE API returned %s employee results", len(result))
return json.loads(result[0])
def get_stores(self, **columns):
"""
Fetch some or all of Store records from CORE.
:returns: A (potentially empty) list of store dict records.
To fetch all stores::
api.get_stores()
To fetch only stores named "Headquarters"::
api.get_stores(description='Headquarters')
"""
params = {
'entity': 'Stores',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return [json.loads(rec) for rec in result]
def get_departments(self, **columns):
"""
Fetch some or all of Department records from CORE.
:returns: A (potentially empty) list of department dict records.
To fetch all departments::
api.get_departments()
To fetch only departments named "Grocery"::
api.get_departments(dept_name='Grocery')
"""
params = {
'entity': 'Departments',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return [json.loads(rec) for rec in result]
def get_department(self, dept_no, **columns):
"""
Fetch an existing Department record from CORE.
:returns: Either a department dict record, or ``None``.
"""
columns['dept_no'] = dept_no
params = {
'entity': 'Departments',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
if result:
if len(result) > 1:
log.warning("CORE API returned %s department results", len(result))
return json.loads(result[0])
def set_department(self, dept_no, **columns):
"""
Update an existing Department record in CORE.
:returns: Boolean indicating success of the operation.
.. note::
Currently this is being used to create a *new* department also. CORE's
``departments`` table does not use auto-increment for its PK, which
means we must provide one even when creating; therefore this method
may be used for that.
"""
columns['dept_no'] = dept_no
params = {
'entity': 'Departments',
'submethod': 'set',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return json.loads(result)
def get_subdepartments(self, **columns):
"""
Fetch some or all of Subdepartment records from CORE.
:returns: A (potentially empty) list of subdepartment dict records.
To fetch all subdepartments::
api.get_subdepartments()
To fetch only subdepartments named "Grocery"::
api.get_subdepartments(subdept_name='Grocery')
"""
params = {
'entity': 'SubDepts',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return [json.loads(rec) for rec in result]
def get_subdepartment(self, subdept_no, **columns):
"""
Fetch an existing Subdepartment record from CORE.
:returns: Either a subdepartment dict record, or ``None``.
"""
columns['subdept_no'] = subdept_no
params = {
'entity': 'SubDepts',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
if result:
if len(result) > 1:
log.warning("CORE API returned %s subdepartment results", len(result))
return json.loads(result[0])
def set_subdepartment(self, subdept_no, **columns):
"""
Update an existing Subdepartment record in CORE.
:returns: Boolean indicating success of the operation.
.. note::
Currently this is being used to create a *new* subdepartment also. CORE's
``subdepartments`` table does not use auto-increment for its PK, which
means we must provide one even when creating; therefore this method
may be used for that.
"""
columns['subdept_no'] = subdept_no
params = {
'entity': 'SubDepts',
'submethod': 'set',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return json.loads(result)
def get_vendors(self, **columns):
"""
Fetch some or all of Vendor records from CORE.
:returns: A (potentially empty) list of vendor dict records.
To fetch all vendors::
api.get_vendors()
To fetch only vendors named "UNFI"::
api.get_vendors(vendorName='UNFI')
"""
params = {
'entity': 'Vendors',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return [json.loads(rec) for rec in result]
def get_vendor(self, vendorID, **columns):
"""
Fetch an existing Vendor record from CORE.
:returns: Either a vendor dict record, or ``None``.
"""
columns['vendorID'] = vendorID
params = {
'entity': 'Vendors',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
if result:
if len(result) > 1:
log.warning("CORE API returned %s vendor results", len(result))
return json.loads(result[0])
def set_vendor(self, vendorID, **columns):
"""
Update an existing Vendor record in CORE.
:returns: Boolean indicating success of the operation.
.. note::
Currently this is being used to create a *new* vendor also. CORE's
``vendors`` table does not use auto-increment for its PK, which
means we must provide one even when creating; therefore this method
may be used for that.
"""
columns['vendorID'] = vendorID
params = {
'entity': 'Vendors',
'submethod': 'set',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return json.loads(result)
def get_products(self, **columns):
"""
Fetch some or all of Product records from CORE.
:returns: A (potentially empty) list of product dict records.
To fetch all products::
api.get_products()
To fetch only products with brand name "Braggs"::
api.get_products(brand='Braggs')
"""
params = {
'entity': 'Products',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return [json.loads(rec) for rec in result]
def get_product(self, upc, **columns):
"""
Fetch an existing Product record from CORE.
:returns: Either a product dict record, or ``None``.
"""
columns['upc'] = upc
params = {
'entity': 'Products',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
if result:
if len(result) > 1:
log.warning("CORE API returned %s product results", len(result))
return json.loads(result[0])
def set_product(self, upc, **columns):
"""
Update an existing Product record in CORE.
:returns: Boolean indicating success of the operation.
.. note::
Currently this is being used to create a *new* product also. CORE's
``products`` table does not use auto-increment for its PK, which
means we must provide one even when creating; therefore this method
may be used for that.
"""
columns['upc'] = upc
params = {
'entity': 'Products',
'submethod': 'set',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return json.loads(result)
def get_vendor_items(self, **columns):
"""
Fetch some or all of VendorItem records from CORE.
:returns: A (potentially empty) list of vendor item dict records.
To fetch all vendor items::
api.get_vendor_items()
To fetch only products with brand name "Braggs"::
api.get_vendor_items(brand='Braggs')
"""
params = {
'entity': 'VendorItems',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return [json.loads(rec) for rec in result]
def get_vendor_item(self, sku, vendorID, **columns):
"""
Fetch an existing VendorItem record from CORE.
:returns: Either a vendor item dict record, or ``None``.
"""
columns['sku'] = sku
columns['vendorID'] = vendorID
params = {
'entity': 'VendorItems',
'submethod': 'get',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
if result:
if len(result) > 1:
log.warning("CORE API returned %s VendorItem results", len(result))
return json.loads(result[0])
def set_vendor_item(self, sku, vendorID, **columns):
"""
Update an existing VendorItem record in CORE.
:returns: Boolean indicating success of the operation.
.. note::
Currently this is being used to create a *new* product also. CORE's
``vendorItems`` table does not use auto-increment for its PK, which
means we must provide one even when creating; therefore this method
may be used for that.
"""
columns['sku'] = sku
columns['vendorID'] = vendorID
params = {
'entity': 'VendorItems',
'submethod': 'set',
'columns': columns,
}
response = self.post(params)
result = self.parse_response(response)
return json.loads(result)

View file

@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2020 Lance Edgar
# Copyright © 2018-2021 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@ -23,12 +23,3 @@
"""
Database Interface
"""
from __future__ import unicode_literals, absolute_import
import warnings
warnings.warn("The `corepos.db` module is deprecated! "
"Please use `corepos.db.office_op` instead.",
DeprecationWarning)
from corepos.db.office_op import *

View file

173
corepos/db/common/op.py Normal file
View file

@ -0,0 +1,173 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2025 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Common schema for operational data models
"""
import sqlalchemy as sa
class ParameterBase:
"""
Base class for Parameter models, shared by Office + Lane.
"""
store_id = sa.Column(sa.SmallInteger(), primary_key=True, nullable=False)
lane_id = sa.Column(sa.SmallInteger(), primary_key=True, nullable=False)
param_key = sa.Column(sa.String(length=100), primary_key=True, nullable=False)
param_value = sa.Column(sa.String(length=255), nullable=True)
is_array = sa.Column(sa.Boolean(), nullable=True)
def __str__(self):
return f"{self.store_id}-{self.lane_id} {self.param_key}"
class EmployeeBase:
"""
Base class for Employee models, shared by Office + Lane.
"""
number = sa.Column('emp_no', sa.SmallInteger(), nullable=False,
primary_key=True, autoincrement=False)
cashier_password = sa.Column('CashierPassword', sa.String(length=50), nullable=True)
admin_password = sa.Column('AdminPassword', sa.String(length=50), nullable=True)
first_name = sa.Column('FirstName', sa.String(length=255), nullable=True)
last_name = sa.Column('LastName', sa.String(length=255), nullable=True)
job_title = sa.Column('JobTitle', sa.String(length=255), nullable=True)
active = sa.Column('EmpActive', sa.Boolean(), nullable=True)
frontend_security = sa.Column('frontendsecurity', sa.SmallInteger(), nullable=True)
backend_security = sa.Column('backendsecurity', sa.SmallInteger(), nullable=True)
birth_date = sa.Column('birthdate', sa.DateTime(), nullable=True)
def __str__(self):
return ' '.join([self.first_name or '', self.last_name or '']).strip()
class ProductBase:
"""
Base class for Product models, shared by Office + Lane.
"""
id = sa.Column(sa.Integer(), nullable=False, primary_key=True, autoincrement=True)
upc = sa.Column(sa.String(length=13), nullable=True)
description = sa.Column(sa.String(length=30), nullable=True)
brand = sa.Column(sa.String(length=30), nullable=True)
formatted_name = sa.Column(sa.String(length=30), nullable=True)
normal_price = sa.Column(sa.Float(), nullable=True)
price_method = sa.Column('pricemethod', sa.SmallInteger(), nullable=True)
group_price = sa.Column('groupprice', sa.Float(), nullable=True)
quantity = sa.Column(sa.SmallInteger(), nullable=True)
special_price = sa.Column(sa.Float(), nullable=True)
special_price_method = sa.Column('specialpricemethod', sa.SmallInteger(), nullable=True)
special_group_price = sa.Column('specialgroupprice', sa.Float(), nullable=True)
special_quantity = sa.Column('specialquantity', sa.SmallInteger(), nullable=True)
special_limit = sa.Column(sa.SmallInteger(), nullable=True)
start_date = sa.Column(sa.DateTime(), nullable=True)
end_date = sa.Column(sa.DateTime(), nullable=True)
department_number = sa.Column('department', sa.SmallInteger(), nullable=True)
size = sa.Column(sa.String(length=9), nullable=True)
tax_rate_id = sa.Column('tax', sa.SmallInteger(), nullable=True)
foodstamp = sa.Column(sa.Boolean(), nullable=True)
scale = sa.Column(sa.Boolean(), nullable=True)
scale_price = sa.Column('scaleprice', sa.Float(), nullable=True)
mix_match_code = sa.Column('mixmatchcode', sa.String(length=13), nullable=True)
created = sa.Column(sa.DateTime(), nullable=True)
modified = sa.Column(sa.DateTime(), nullable=True)
tare_weight = sa.Column('tareweight', sa.Float(), nullable=True)
discount = sa.Column(sa.SmallInteger(), nullable=True)
discount_type = sa.Column('discounttype', sa.SmallInteger(), nullable=True)
line_item_discountable = sa.Column(sa.Boolean(), nullable=True)
unit_of_measure = sa.Column('unitofmeasure', sa.String(length=15), nullable=True)
wicable = sa.Column(sa.SmallInteger(), nullable=True)
quantity_enforced = sa.Column('qttyEnforced', sa.Boolean(), nullable=True)
id_enforced = sa.Column('idEnforced', sa.SmallInteger(), nullable=True)
cost = sa.Column(sa.Float(), nullable=True)
special_cost = sa.Column(sa.Float(), nullable=True)
received_cost = sa.Column(sa.Float(), nullable=True)
in_use = sa.Column('inUse', sa.Boolean(), nullable=True)
numflag = sa.Column(sa.Integer(), nullable=True)
subdepartment_number = sa.Column('subdept', sa.SmallInteger(), nullable=True)
deposit = sa.Column(sa.Float(), nullable=True)
local = sa.Column(sa.Integer(), nullable=True, default=0)
store_id = sa.Column(sa.SmallInteger(), nullable=True)
default_vendor_id = sa.Column(sa.Integer(), nullable=True)
current_origin_id = sa.Column(sa.Integer(), nullable=True)
auto_par = sa.Column(sa.Float(), nullable=True, default=0)
price_rule_id = sa.Column(sa.Integer(), nullable=True, default=0)
last_sold = sa.Column(sa.DateTime(), nullable=True)

114
corepos/db/common/trans.py Normal file
View file

@ -0,0 +1,114 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2025 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Common schema for transaction data models
"""
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.declarative import declared_attr
class TransactionDetailBase:
"""
Base class for POS transaction detail models, shared by Office +
Lane.
"""
# register
register_no = sa.Column(sa.Integer(), nullable=True)
# txn
trans_id = sa.Column(sa.Integer(), nullable=True)
trans_no = sa.Column(sa.Integer(), nullable=True)
trans_type = sa.Column(sa.String(length=1), nullable=True)
trans_subtype = sa.Column(sa.String(length=2), nullable=True)
trans_status = sa.Column(sa.String(length=1), nullable=True)
# cashier
emp_no = sa.Column(sa.Integer(), nullable=True)
# customer
card_no = sa.Column(sa.Integer(), nullable=True)
memType = sa.Column(sa.Integer(), nullable=True)
staff = sa.Column(sa.Boolean(), nullable=True)
##############################
# remainder is "line item" ...
##############################
upc = sa.Column(sa.String(length=13), nullable=True)
department = sa.Column(sa.Integer(), nullable=True)
description = sa.Column(sa.String(length=30), nullable=True)
quantity = sa.Column(sa.Float(), nullable=True)
scale = sa.Column(sa.Boolean(), nullable=True, default=False)
cost = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
unitPrice = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
total = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
regPrice = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
tax = sa.Column(sa.SmallInteger(), nullable=True)
foodstamp = sa.Column(sa.Boolean(), nullable=True)
discount = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
memDiscount = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
discountable = sa.Column(sa.Boolean(), nullable=True)
discounttype = sa.Column(sa.Integer(), nullable=True)
voided = sa.Column(sa.Integer(), nullable=True)
percentDiscount = sa.Column(sa.Integer(), nullable=True)
ItemQtty = sa.Column(sa.Float(), nullable=True)
volDiscType = sa.Column(sa.Integer(), nullable=True)
volume = sa.Column(sa.Integer(), nullable=True)
VolSpecial = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
mixMatch = sa.Column(sa.String(length=13), nullable=True)
matched = sa.Column(sa.Boolean(), nullable=True)
numflag = sa.Column(sa.Integer(), nullable=True, default=0)
charflag = sa.Column(sa.String(length=2), nullable=True)
def __str__(self):
txnid = '-'.join([str(val) for val in [self.register_no,
self.trans_no,
self.trans_id]])
return f"{txnid} {self.description or ''}"

View file

@ -0,0 +1,30 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2021 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
"Lane Operational" Database Interface
"""
from sqlalchemy import orm
Session = orm.sessionmaker()

167
corepos/db/lane_op/model.py Normal file
View file

@ -0,0 +1,167 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2025 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Data model for CORE POS "lane_op" DB
"""
import sqlalchemy as sa
from sqlalchemy import orm
from corepos.db.common import op as common
Base = orm.declarative_base()
class Parameter(common.ParameterBase, Base):
"""
Data model for ``parameters`` table.
"""
__tablename__ = 'parameters'
class Employee(common.EmployeeBase, Base):
"""
Data model for ``employees`` table.
"""
__tablename__ = 'employees'
class Department(Base):
"""
Represents a department within the organization.
"""
__tablename__ = 'departments'
number = sa.Column('dept_no', sa.SmallInteger(), nullable=False,
primary_key=True, autoincrement=False)
name = sa.Column('dept_name', sa.String(length=30), nullable=True)
tax = sa.Column('dept_tax', sa.Boolean(), nullable=True)
food_stampable = sa.Column('dept_fs', sa.Boolean(), nullable=True)
limit = sa.Column('dept_limit', sa.Float(), nullable=True)
minimum = sa.Column('dept_minimum', sa.Float(), nullable=True)
discount = sa.Column('dept_discount', sa.Boolean(), nullable=True)
see_id = sa.Column('dept_see_id', sa.SmallInteger(), nullable=True)
modified = sa.Column(sa.DateTime(), nullable=True)
modified_by_id = sa.Column('modifiedby', sa.Integer(), nullable=True)
margin = sa.Column(sa.Float(), nullable=False)
sales_code = sa.Column('salesCode', sa.Integer(), nullable=False)
member_only = sa.Column('memberOnly', sa.SmallInteger(), nullable=False)
line_item_discount = sa.Column(sa.Boolean(), nullable=True)
wicable = sa.Column('dept_wicable', sa.Boolean(), nullable=True)
def __str__(self):
return self.name or ""
class Product(common.ProductBase, Base):
"""
Data model for ``products`` table.
"""
__tablename__ = 'products'
class CustomerClassic(Base):
"""
Represents a customer of the organization.
https://github.com/CORE-POS/IS4C/blob/master/pos/is4c-nf/lib/models/op/CustdataModel.php
"""
__tablename__ = 'custdata'
# __table_args__ = (
# sa.ForeignKeyConstraint(['memType'], ['memtype.memtype']),
# )
id = sa.Column(sa.Integer(), nullable=False, primary_key=True, autoincrement=True)
card_number = sa.Column('CardNo', sa.Integer(), nullable=True)
person_number = sa.Column('personNum', sa.SmallInteger(), nullable=True)
first_name = sa.Column('FirstName', sa.String(length=30), nullable=True)
last_name = sa.Column('LastName', sa.String(length=30), nullable=True)
cash_back = sa.Column('CashBack', sa.Numeric(precision=10, scale=2), nullable=True)
balance = sa.Column('Balance', sa.Numeric(precision=10, scale=2), nullable=True)
discount = sa.Column('Discount', sa.SmallInteger(), nullable=True)
member_discount_limit = sa.Column('MemDiscountLimit', sa.Numeric(precision=10, scale=2), nullable=True)
charge_limit = sa.Column('ChargeLimit', sa.Numeric(precision=10, scale=2), nullable=True)
charge_ok = sa.Column('ChargeOk', sa.Boolean(), nullable=True, default=True)
write_checks = sa.Column('WriteChecks', sa.Boolean(), nullable=True, default=True)
store_coupons = sa.Column('StoreCoupons', sa.Boolean(), nullable=True, default=True)
type = sa.Column('Type', sa.String(length=10), nullable=True, default='PC')
member_type_id = sa.Column('memType', sa.SmallInteger(), nullable=True)
# member_type = orm.relationship(
# MemberType,
# primaryjoin=MemberType.id == member_type_id,
# foreign_keys=[member_type_id],
# doc="""
# Reference to the :class:`MemberType` to which this member belongs.
# """)
staff = sa.Column(sa.Boolean(), nullable=True, default=False)
ssi = sa.Column('SSI', sa.Boolean(), nullable=True, default=False)
purchases = sa.Column('Purchases', sa.Numeric(precision=10, scale=2), nullable=True, default=0)
number_of_checks = sa.Column('NumberOfChecks', sa.SmallInteger(), nullable=True, default=0)
member_coupons = sa.Column('memCoupons', sa.Integer(), nullable=True, default=1)
blue_line = sa.Column('blueLine', sa.String(length=50), nullable=True)
shown = sa.Column('Shown', sa.Boolean(), nullable=True, default=True)
last_change = sa.Column('LastChange', sa.DateTime(), nullable=True)
def __str__(self):
return "{} {}".format(self.first_name or '', self.last_name or '').strip()
# TODO: deprecate / remove this
CustData = CustomerClassic

View file

@ -0,0 +1,30 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2025 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Lane Transaction Database
"""
from sqlalchemy import orm
Session = orm.sessionmaker()

View file

@ -0,0 +1,79 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2025 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Data model for CORE POS "lane_trans" DB
"""
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.declarative import declared_attr
from corepos.db.common import trans as common
Base = orm.declarative_base()
class DTransactionBase(common.TransactionDetailBase):
"""
Base class for ``dtransactions`` and similar models.
"""
pos_row_id = sa.Column(sa.Integer(), primary_key=True, nullable=False)
store_id = sa.Column(sa.Integer(), nullable=True, default=0)
date_time = sa.Column('datetime', sa.DateTime(), nullable=True)
class DTransaction(DTransactionBase, Base):
"""
Data model for ``dtransactions`` table.
"""
__tablename__ = 'dtransactions'
class LocalTransBase(common.TransactionDetailBase):
"""
Base class for ``localtrans`` and similar models.
"""
@declared_attr
def __table_args__(self):
return (
sa.PrimaryKeyConstraint('trans_id'),
)
date_time = sa.Column('datetime', sa.DateTime(), nullable=True)
class LocalTrans(LocalTransBase, Base):
"""
Data model for ``localtrans`` table.
"""
__tablename__ = 'localtrans'
class LocalTempTrans(LocalTransBase, Base):
"""
Data model for ``localtemptrans`` table.
"""
__tablename__ = 'localtemptrans'

View file

@ -0,0 +1,30 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2023 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
"Archive" Transaction Database Interface
"""
from sqlalchemy import orm
Session = orm.sessionmaker()

View file

@ -0,0 +1,63 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2025 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
CORE Office "arch" data model
"""
import sqlalchemy as sa
from sqlalchemy import orm
from corepos.db.common import trans as common
from corepos.db.office_trans.model import DTransactionBase
Base = orm.declarative_base()
class BigArchive(DTransactionBase, Base):
"""
Data model for ``bigArchive`` table.
"""
__tablename__ = 'bigArchive'
# TODO: deprecate / remove this
TransactionDetail = BigArchive
class DLogBase(common.TransactionDetailBase):
"""
Base class for ``dlogBig`` and similar models.
"""
store_row_id = sa.Column(sa.Integer(), primary_key=True, nullable=False)
store_id = sa.Column(sa.Integer(), nullable=True, default=0)
pos_row_id = sa.Column(sa.Integer(), nullable=True)
date_time = sa.Column('tdate', sa.DateTime(), nullable=True)
class DLogBig(DLogBase, Base):
"""
Data model for ``dlogBig`` view.
"""
__tablename__ = 'dlogBig'

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2020 Lance Edgar
# Copyright © 2018-2025 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@ -24,102 +24,68 @@
CORE POS Transaction Data Model
"""
from __future__ import unicode_literals, absolute_import
import six
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import orm
from corepos.db.common import trans as common
Base = declarative_base()
Base = orm.declarative_base()
@six.python_2_unicode_compatible
class TransactionDetail(Base):
# TODO: not sure what primary key should be for this? am trying a
# composite one so far, we'll see...cf. also andy's comments in
# https://github.com/CORE-POS/IS4C/pull/1189#issuecomment-1597481138
class StockPurchase(Base):
"""
Represents a POS transaction detail record.
Represents a member equity payment.
"""
__tablename__ = 'stockpurchases'
card_number = sa.Column('card_no', sa.Integer(), nullable=False, primary_key=True, autoincrement=False)
amount = sa.Column('stockPurchase', sa.Numeric(precision=10, scale=2), nullable=True)
datetime = sa.Column('tdate', sa.DateTime(), nullable=True, primary_key=True, autoincrement=False)
transaction_number = sa.Column('trans_num', sa.String(length=50), nullable=True, primary_key=True)
transaction_id = sa.Column('trans_id', sa.Integer(), nullable=True)
department_number = sa.Column('dept', sa.Integer(), nullable=True, primary_key=True, autoincrement=False)
def __str__(self):
return f"#{self.card_number} for ${self.amount}"
class EquityLiveBalance(Base):
__tablename__ = 'equity_live_balance'
member_number = sa.Column('memnum', sa.Integer(), nullable=False, primary_key=True, autoincrement=False)
payments = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
start_date = sa.Column('startdate', sa.DateTime(), nullable=True)
class DTransactionBase(common.TransactionDetailBase):
"""
Base class for ``dtransactions`` and similar models.
"""
store_row_id = sa.Column(sa.Integer(), primary_key=True, nullable=False)
pos_row_id = sa.Column(sa.Integer(), nullable=True)
store_id = sa.Column(sa.Integer(), nullable=True, default=0)
date_time = sa.Column('datetime', sa.DateTime(), nullable=True)
class DTransaction(DTransactionBase, Base):
"""
Data model for ``dtransactions`` table.
"""
__tablename__ = 'dtransactions'
# store
store_row_id = sa.Column(sa.Integer(), primary_key=True, nullable=False)
store_id = sa.Column(sa.Integer(), nullable=True, default=0)
# register
register_number = sa.Column('register_no', sa.Integer(), nullable=True)
pos_row_id = sa.Column(sa.Integer(), nullable=True)
# txn
transaction_id = sa.Column('trans_id', sa.Integer(), nullable=True)
transaction_number = sa.Column('trans_no', sa.Integer(), nullable=True)
transaction_type = sa.Column('trans_type', sa.String(length=1), nullable=True)
transaction_subtype = sa.Column('trans_subtype', sa.String(length=2), nullable=True)
transaction_status = sa.Column('trans_status', sa.String(length=1), nullable=True)
# timestamps
date_time = sa.Column('datetime', sa.DateTime(), nullable=True)
# cashier
employee_number = sa.Column('emp_no', sa.Integer(), nullable=True)
# customer
card_number = sa.Column('card_no', sa.Integer(), nullable=True)
member_type = sa.Column('memType', sa.Integer(), nullable=True)
staff = sa.Column(sa.Boolean(), nullable=True)
##############################
# remainder is "line item" ...
##############################
upc = sa.Column(sa.String(length=13), nullable=True)
department_number = sa.Column('department', sa.Integer(), nullable=True)
description = sa.Column(sa.String(length=30), nullable=True)
quantity = sa.Column(sa.Float(), nullable=True)
scale = sa.Column(sa.Boolean(), nullable=True, default=False)
cost = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
unit_price = sa.Column('unitPrice', sa.Numeric(precision=10, scale=2), nullable=True)
total = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
reg_price = sa.Column('regPrice', sa.Numeric(precision=10, scale=2), nullable=True)
tax = sa.Column(sa.Boolean(), nullable=True)
food_stamp = sa.Column('foodstamp', sa.Boolean(), nullable=True)
discount = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
member_discount = sa.Column('memDiscount', sa.Numeric(precision=10, scale=2), nullable=True)
discountable = sa.Column(sa.Boolean(), nullable=True)
discount_type = sa.Column('discounttype', sa.Integer(), nullable=True)
voided = sa.Column(sa.Boolean(), nullable=True)
percent_discount = sa.Column('percentDiscount', sa.Integer(), nullable=True)
item_quantity = sa.Column('ItemQtty', sa.Float(), nullable=True)
volume_discount_type = sa.Column('volDiscType', sa.Integer(), nullable=True)
volume = sa.Column(sa.Integer(), nullable=True)
volume_special = sa.Column('VolSpecial', sa.Numeric(precision=10, scale=2), nullable=True)
mix_match = sa.Column('mixMatch', sa.String(length=13), nullable=True)
matched = sa.Column(sa.Boolean(), nullable=True)
num_flag = sa.Column('numflag', sa.Integer(), nullable=True, default=0)
char_flag = sa.Column('charflag', sa.String(length=2), nullable=True)
def __str__(self):
return self.description or ''
# TODO: deprecate / remove this
TransactionDetail = DTransaction

View file

@ -0,0 +1,32 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2023 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
"Archive" Transaction Database Interface
"""
import warnings
warnings.warn("The `corepos.db.office_trans_archive` module is deprecated! "
"Please use `corepos.db.office_arch` instead.",
DeprecationWarning, stacklevel=2)
from corepos.db.office_arch import *

View file

@ -0,0 +1,32 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2023 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
CORE POS Transaction Data Model
"""
import warnings
warnings.warn("The `corepos.db.office_trans_archive.model` module is deprecated! "
"Please use `corepos.db.office_arch.model` instead.",
DeprecationWarning, stacklevel=2)
from corepos.db.office_arch.model import *

View file

@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2019 Lance Edgar
# Copyright © 2018-2020 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@ -24,11 +24,9 @@
CORE POS Database Utilities
"""
from __future__ import unicode_literals, absolute_import
import sqlalchemy as sa
from corepos.db import model as corepos
from corepos.db.office_op import model as corepos
def get_last_card_number(session):
@ -38,3 +36,25 @@ def get_last_card_number(session):
"""
return session.query(sa.func.max(corepos.Customer.card_number))\
.scalar() or 0
def table_exists(session, model_class):
"""
Determine if a table exists in the database.
:param session: SQLAlchemy session object, opened against the database in
question.
:param model_class: The model class associated with the table in question.
:returns: Boolean indicating if the table exists.
"""
try:
session.query(model_class).count()
except sa.exc.ProgrammingError as error:
if "doesn't exist" in str(error):
return False
else:
raise
else:
return True

View file

@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2019 Lance Edgar
# Copyright © 2018-2024 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@ -24,12 +24,44 @@
CORE POS enumeration constants
"""
from __future__ import unicode_literals, absolute_import
try:
from collections import OrderedDict
except ImportError:
from ordereddict import OrderedDict
from enum import Enum
class CoreDbType(str, Enum):
office_op = 'office_op'
office_trans = 'office_trans'
office_arch = 'office_arch'
BATCH_DISCOUNT_TYPE_TRACKING = -1
BATCH_DISCOUNT_TYPE_PRICE_CHANGE = 0
BATCH_DISCOUNT_TYPE_SALE_EVERYONE = 1
BATCH_DISCOUNT_TYPE_SALE_RESTRICTED = 2
BATCH_DISCOUNT_TYPE_SLIDING_PERCENT = 3
BATCH_DISCOUNT_TYPE_SLIDING_AMOUNT = 5
BATCH_DISCOUNT_TYPE = OrderedDict([
(BATCH_DISCOUNT_TYPE_PRICE_CHANGE, "None (Change regular price)"),
(BATCH_DISCOUNT_TYPE_SALE_EVERYONE, "Sale for everyone"),
(BATCH_DISCOUNT_TYPE_SALE_RESTRICTED, "Sale for Members"),
(BATCH_DISCOUNT_TYPE_SLIDING_PERCENT, "Sliding % Off for Members"),
(BATCH_DISCOUNT_TYPE_SLIDING_AMOUNT, "Sliding $ Off for Members"),
(BATCH_DISCOUNT_TYPE_TRACKING, "Tracking (does not change any prices)"),
])
BATCH_EDITOR_UI_STANDARD = 1
BATCH_EDITOR_UI_PAIRED_SALE = 2
BATCH_EDITOR_UI_PARTIAL = 3
BATCH_EDITOR_UI_TRACKING = 4
BATCH_EDITOR_UI = OrderedDict([
(BATCH_EDITOR_UI_STANDARD, "Standard"),
(BATCH_EDITOR_UI_PAIRED_SALE, "Paired Sale"),
(BATCH_EDITOR_UI_PARTIAL, "Partial"),
(BATCH_EDITOR_UI_TRACKING, "Tracking"),
])
HOUSE_COUPON_MEMBER_ONLY_NO = 0
@ -92,3 +124,14 @@ MEMBER_CONTACT_PREFERENCE = OrderedDict([
(MEMBER_CONTACT_PREFERENCE_EMAIL_ONLY, "email only"),
(MEMBER_CONTACT_PREFERENCE_BOTH, "both (postal mail and email)"),
])
PRODUCT_PRICE_METHOD_DISABLED = 0
PRODUCT_PRICE_METHOD_ALWAYS = 1
PRODUCT_PRICE_METHOD_FULL_SETS = 2
PRODUCT_PRICE_METHOD = OrderedDict([
(PRODUCT_PRICE_METHOD_DISABLED, "Disabled"),
(PRODUCT_PRICE_METHOD_ALWAYS, "Always use this price"),
(PRODUCT_PRICE_METHOD_FULL_SETS, "Use this price for full sets"),
])

49
pyproject.toml Normal file
View file

@ -0,0 +1,49 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "pyCOREPOS"
version = "0.5.1"
description = "Python Interface to CORE POS"
readme = "README.md"
authors = [{name = "Lance Edgar", email = "lance@edbob.org"}]
license = {text = "GNU GPL v3+"}
classifiers = [
"Development Status :: 3 - Alpha",
"Environment :: Console",
"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 = [
"mysql-connector-python",
"requests",
"SQLAlchemy>=1.4",
]
[project.urls]
Homepage = "https://forgejo.wuttaproject.org/rattail/pycorepos"
Repository = "https://forgejo.wuttaproject.org/rattail/pycorepos"
Issues = "https://forgejo.wuttaproject.org/rattail/pycorepos/issues"
Changelog = "https://forgejo.wuttaproject.org/rattail/pycorepos/src/branch/master/CHANGELOG.md"
[tool.commitizen]
version_provider = "pep621"
tag_format = "v$version"
update_changelog_on_bump = true
[tool.hatch.build.targets.wheel]
packages = ["corepos"]

View file

@ -1,96 +0,0 @@
# -*- coding: utf-8; -*-
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2020 Lance Edgar
#
# This file is part of pyCOREPOS.
#
# pyCOREPOS 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.
#
# pyCOREPOS 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
# pyCOREPOS. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
import os
import sys
from setuptools import setup, find_packages
here = os.path.abspath(os.path.dirname(__file__))
exec(open(os.path.join(here, 'corepos', '_version.py')).read())
README = open(os.path.join(here, 'README.rst')).read()
requires = [
#
# Version numbers within comments below have specific meanings.
# Basically the 'low' value is a "soft low," and 'high' a "soft high."
# In other words:
#
# If either a 'low' or 'high' value exists, the primary point to be
# made about the value is that it represents the most current (stable)
# version available for the package (assuming typical public access
# methods) whenever this project was started and/or documented.
# Therefore:
#
# If a 'low' version is present, you should know that attempts to use
# versions of the package significantly older than the 'low' version
# may not yield happy results. (A "hard" high limit may or may not be
# indicated by a true version requirement.)
#
# Similarly, if a 'high' version is present, and especially if this
# project has laid dormant for a while, you may need to refactor a bit
# when attempting to support a more recent version of the package. (A
# "hard" low limit should be indicated by a true version requirement
# when a 'high' version is present.)
#
# In any case, developers and other users are encouraged to play
# outside the lines with regard to these soft limits. If bugs are
# encountered then they should be filed as such.
#
# package # low high
'mysql-connector-python', # 8.0.6
'six', # 1.12.0
'SQLAlchemy', # 0.9.8
]
setup(
name = "pyCOREPOS",
version = __version__,
author = "Lance Edgar",
author_email = "lance@edbob.org",
url = "https://rattailproject.org/",
license = "GNU GPL v3",
description = "Python Interface to CORE POS",
long_description = README,
classifiers = [
'Development Status :: 3 - Alpha',
'Environment :: Console',
'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',
],
install_requires = requires,
packages = find_packages(),
)

View file

@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018 Lance Edgar
# Copyright © 2018-2024 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@ -24,17 +24,33 @@
Tasks for 'pyCOREPOS' package
"""
from __future__ import unicode_literals, absolute_import
import os
import re
import shutil
from invoke import task
here = os.path.abspath(os.path.dirname(__file__))
__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 'pyCOREPOS'.
"""
if os.path.exists('pyCOREPOS.egg-info'):
shutil.rmtree('pyCOREPOS.egg-info')
ctx.run('python setup.py sdist --formats=gztar upload')
c.run('python -m build --sdist')
c.run('twine upload dist/pycorepos-{}.tar.gz'.format(__version__))