diff --git a/.gitignore b/.gitignore
index 07ddefb..9a52e5b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1 @@
-*~
-*.pyc
-dist/
pyCOREPOS.egg-info/
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2c26baa..881ced9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,141 +5,6 @@ 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.
diff --git a/README.md b/README.md
deleted file mode 100644
index dd69797..0000000
--- a/README.md
+++ /dev/null
@@ -1,5 +0,0 @@
-
-# pyCOREPOS
-
-A Python interface to the [CORE POS](https://github.com/CORE-POS)
-system.
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..0b34d2f
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,7 @@
+
+pyCOREPOS
+=========
+
+A Python interface to the `CORE POS`_ system.
+
+.. _CORE POS: https://github.com/CORE-POS
diff --git a/corepos/_version.py b/corepos/_version.py
index 555fee3..4e18c2b 100644
--- a/corepos/_version.py
+++ b/corepos/_version.py
@@ -1,6 +1,3 @@
# -*- coding: utf-8; -*-
-from importlib.metadata import version
-
-
-__version__ = version('pyCOREPOS')
+__version__ = '0.1.8'
diff --git a/corepos/api.py b/corepos/api.py
index a24b906..e7310a0 100644
--- a/corepos/api.py
+++ b/corepos/api.py
@@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
-# Copyright © 2018-2024 Lance Edgar
+# Copyright © 2018-2021 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@@ -28,7 +28,6 @@ import json
import logging
import requests
-from requests.auth import HTTPDigestAuth
log = logging.getLogger(__name__)
@@ -49,42 +48,27 @@ class CoreAPIError(Exception):
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,
- ):
+ def __init__(self, url, verify=True):
+ """
+ Constructor for the API client.
+
+ :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.
+ """
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
@@ -104,8 +88,8 @@ class CoreWebAPI(object):
'id': 1,
}
- response = self.session.post(self.url, data=json.dumps(payload),
- verify=self.verify)
+ response = requests.post(self.url, data=json.dumps(payload),
+ verify=self.verify)
response.raise_for_status()
return response
@@ -132,22 +116,6 @@ class CoreWebAPI(object):
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.
@@ -198,40 +166,6 @@ class CoreWebAPI(object):
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.
diff --git a/corepos/db/common/__init__.py b/corepos/db/common/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/corepos/db/common/op.py b/corepos/db/common/op.py
deleted file mode 100644
index 30ecc1c..0000000
--- a/corepos/db/common/op.py
+++ /dev/null
@@ -1,173 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-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)
diff --git a/corepos/db/common/trans.py b/corepos/db/common/trans.py
deleted file mode 100644
index 9ec5601..0000000
--- a/corepos/db/common/trans.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-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 ''}"
diff --git a/corepos/db/lane_op/model.py b/corepos/db/lane_op/model.py
index 456b1b8..80051ab 100644
--- a/corepos/db/lane_op/model.py
+++ b/corepos/db/lane_op/model.py
@@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
-# Copyright © 2018-2025 Lance Edgar
+# Copyright © 2018-2021 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@@ -25,26 +25,11 @@ Data model for CORE POS "lane_op" DB
"""
import sqlalchemy as sa
-from sqlalchemy import orm
-
-from corepos.db.common import op as common
+# from sqlalchemy import orm
+from sqlalchemy.ext.declarative import declarative_base
-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'
+Base = declarative_base()
class Department(Base):
@@ -88,14 +73,144 @@ class Department(Base):
return self.name or ""
-class Product(common.ProductBase, Base):
+class Product(Base):
"""
- Data model for ``products`` table.
+ Represents a product, purchased and/or sold by the organization.
"""
__tablename__ = 'products'
+ # __table_args__ = (
+ # sa.ForeignKeyConstraint(['department'], ['departments.dept_no']),
+ # sa.ForeignKeyConstraint(['subdept'], ['subdepts.subdept_no']),
+ # sa.ForeignKeyConstraint(['tax'], ['taxrates.id']),
+ # )
+
+ 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)
+ # department = orm.relationship(
+ # Department,
+ # primaryjoin=Department.number == department_number,
+ # foreign_keys=[department_number],
+ # doc="""
+ # Reference to the :class:`Department` to which the product belongs.
+ # """)
+
+ size = sa.Column(sa.String(length=9), nullable=True)
+
+ tax_rate_id = sa.Column('tax', sa.SmallInteger(), nullable=True)
+ # tax_rate = orm.relationship(TaxRate)
+
+ 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)
+
+ # TODO: what to do about this 'replaces' thing?
+ # 'batchID'=>array('type'=>'TINYINT', 'replaces'=>'advertised'),
+ # batch_id = sa.Column('batchID', sa.SmallInteger(), nullable=True)
+ # advertised = sa.Column(sa.Boolean(), 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)
+
+ flags = sa.Column('numflag', sa.Integer(), nullable=True)
+
+ subdepartment_number = sa.Column('subdept', sa.SmallInteger(), nullable=True)
+ # subdepartment = orm.relationship(
+ # Subdepartment,
+ # primaryjoin=Subdepartment.number == subdepartment_number,
+ # foreign_keys=[subdepartment_number],
+ # doc="""
+ # Reference to the :class:`Subdepartment` to which the product belongs.
+ # """)
+
+ deposit = sa.Column(sa.Float(), nullable=True)
+
+ local = sa.Column(sa.Integer(), nullable=True,
+ default=0) # TODO: do we want a default here?
+
+ store_id = sa.Column(sa.SmallInteger(), nullable=True)
+
+ default_vendor_id = sa.Column(sa.Integer(), nullable=True)
+ # default_vendor = orm.relationship(
+ # Vendor,
+ # primaryjoin=Vendor.id == default_vendor_id,
+ # foreign_keys=[default_vendor_id],
+ # doc="""
+ # Reference to the default :class:`Vendor` from which the product is obtained.
+ # """)
+
+ current_origin_id = sa.Column(sa.Integer(), nullable=True)
+
+ auto_par = sa.Column(sa.Float(), nullable=True,
+ default=0) # TODO: do we want a default here?
+
+ price_rule_id = sa.Column(sa.Integer(), nullable=True)
+
+ # TODO: some older DB's might not have this? guess we'll see
+ last_sold = sa.Column(sa.DateTime(), nullable=True)
-class CustomerClassic(Base):
+class CustData(Base):
"""
Represents a customer of the organization.
@@ -161,7 +276,3 @@ class CustomerClassic(Base):
def __str__(self):
return "{} {}".format(self.first_name or '', self.last_name or '').strip()
-
-
-# TODO: deprecate / remove this
-CustData = CustomerClassic
diff --git a/corepos/db/lane_trans/__init__.py b/corepos/db/lane_trans/__init__.py
deleted file mode 100644
index 8e8c706..0000000
--- a/corepos/db/lane_trans/__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-Lane Transaction Database
-"""
-
-from sqlalchemy import orm
-
-
-Session = orm.sessionmaker()
diff --git a/corepos/db/lane_trans/model.py b/corepos/db/lane_trans/model.py
deleted file mode 100644
index f2245f5..0000000
--- a/corepos/db/lane_trans/model.py
+++ /dev/null
@@ -1,79 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-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'
diff --git a/corepos/db/office_arch/__init__.py b/corepos/db/office_arch/__init__.py
deleted file mode 100644
index 70292e9..0000000
--- a/corepos/db/office_arch/__init__.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-"Archive" Transaction Database Interface
-"""
-
-from sqlalchemy import orm
-
-
-Session = orm.sessionmaker()
diff --git a/corepos/db/office_arch/model.py b/corepos/db/office_arch/model.py
deleted file mode 100644
index bc5838f..0000000
--- a/corepos/db/office_arch/model.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# -*- 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 .
-#
-################################################################################
-"""
-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'
diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py
index 60ad478..107da66 100644
--- a/corepos/db/office_op/model.py
+++ b/corepos/db/office_op/model.py
@@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
-# Copyright © 2018-2025 Lance Edgar
+# Copyright © 2018-2021 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@@ -29,14 +29,13 @@ import logging
import sqlalchemy as sa
from sqlalchemy import orm
+from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
-from corepos.db.common import op as common
-
log = logging.getLogger(__name__)
-Base = orm.declarative_base()
+Base = declarative_base()
class StringableDateTime(sa.TypeDecorator):
@@ -56,12 +55,42 @@ class StringableDateTime(sa.TypeDecorator):
raise NotImplementedError
-class Parameter(common.ParameterBase, Base):
+class Change(Base):
"""
- Data model for ``parameters`` table.
+ Represents a changed (or deleted) record, which is pending synchronization
+ to another system(s).
+
+ .. note::
+ This table may or may not be installed to a given CORE Office Op DB. Its
+ presence is required if Rattail datasync needs to "watch" the DB.
+ """
+ __tablename__ = 'datasync_changes'
+
+ id = sa.Column(sa.Integer(), nullable=False, primary_key=True)
+ object_type = sa.Column(sa.String(length=255), nullable=False)
+ object_key = sa.Column(sa.String(length=255), nullable=False)
+ deleted = sa.Column(sa.Boolean(), nullable=False, default=False)
+
+
+class Parameter(Base):
+ """
+ Represents a "parameter" value.
"""
__tablename__ = 'parameters'
+ 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 "{}-{} {}".format(self.store_id, self.lane_id, self.param_key)
+
class TableSyncRule(Base):
"""
@@ -69,9 +98,7 @@ class TableSyncRule(Base):
"""
__tablename__ = 'TableSyncRules'
- # nb. this should be autoincrement, but we can't do that
- # automatically via sqlalchemy when PK is composite
- id = sa.Column('tableSyncRuleID', sa.Integer(), nullable=False, primary_key=True)
+ id = sa.Column('tableSyncRuleID', sa.Integer(), nullable=False, primary_key=True, autoincrement=True)
table_name = sa.Column('tableName', sa.String(length=255), nullable=False, primary_key=True)
@@ -160,25 +187,6 @@ class Store(Base):
return self.description or ""
-class MasterSuperDepartment(Base):
- """
- A department may belong to more than one superdepartment, but has
- one "master" superdepartment. This avoids duplicating rows in
- some reports. By convention, a department's "master"
- superdepartment is the one with the lowest superID.
- """
- __tablename__ = 'MasterSuperDepts'
-
- super_id = sa.Column('superID', sa.Integer(), primary_key=True, autoincrement=False, nullable=False)
-
- department_id = sa.Column('dept_ID', sa.Integer(), primary_key=True, autoincrement=False, nullable=False)
-
- super_name = sa.Column(sa.String(length=50), nullable=True)
-
- def __str__(self):
- return self.super_name or ""
-
-
class SuperDepartment(Base):
"""
Represents a "super" (parent/child) department mapping.
@@ -218,25 +226,15 @@ class Department(Base):
Represents a department within the organization.
"""
__tablename__ = 'departments'
- __table_args__ = (
- sa.ForeignKeyConstraint(['dept_tax'], ['taxrates.id']),
- )
number = sa.Column('dept_no', sa.SmallInteger(), primary_key=True, autoincrement=False, nullable=False)
name = sa.Column('dept_name', sa.String(length=30), nullable=True)
- tax_rate_id = sa.Column('dept_tax', sa.SmallInteger(), nullable=True)
- tax_rate = orm.relationship('TaxRate')
- # TODO: deprecate / remove this
- tax = orm.synonym('tax_rate_id')
+ tax = sa.Column('dept_tax', sa.Boolean(), nullable=True)
food_stampable = sa.Column('dept_fs', sa.Boolean(), nullable=True)
- wicable = sa.Column('dept_wicable', sa.SmallInteger(), nullable=True)
-
- active = sa.Column(sa.Boolean(), default=True)
-
limit = sa.Column('dept_limit', sa.Float(), nullable=True)
minimum = sa.Column('dept_minimum', sa.Float(), nullable=True)
@@ -536,7 +534,7 @@ class Origin(Base):
return self.name or self.short_name or ""
-class Product(common.ProductBase, Base):
+class Product(Base):
"""
Represents a product, purchased and/or sold by the organization.
"""
@@ -547,68 +545,125 @@ class Product(common.ProductBase, Base):
sa.ForeignKeyConstraint(['tax'], ['taxrates.id']),
)
+ id = sa.Column(sa.Integer(), primary_key=True, autoincrement=True, nullable=False)
+
+ 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)
+
+ 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)
department = orm.relationship(
Department,
- primaryjoin='Department.number == Product.department_number',
- foreign_keys='Product.department_number',
+ primaryjoin=Department.number == department_number,
+ foreign_keys=[department_number],
doc="""
Reference to the :class:`Department` to which the product belongs.
""")
+ size = sa.Column(sa.String(length=9), nullable=True)
+
+ tax_rate_id = sa.Column('tax', sa.SmallInteger(), nullable=True)
tax_rate = orm.relationship(TaxRate)
+ foodstamp = sa.Column(sa.Boolean(), nullable=True)
+
+ scale = sa.Column(sa.Boolean(), nullable=True)
+
+ # TODO: yikes, did i just code this all wrong the first time? pretty sure
+ # this needs to change to a decimal column...
+ scale_price = sa.Column('scaleprice', sa.Boolean(), nullable=True)
+ # scale_price = sa.Column('scaleprice', sa.Numeric(precision=10, scale=2), nullable=True)
+
+ mix_match_code = sa.Column('mixmatchcode', sa.String(length=13), nullable=True)
+
+ created = sa.Column(StringableDateTime(), nullable=True)
+
+ modified = sa.Column(sa.DateTime(), nullable=True)
+
# advertised = sa.Column(sa.Boolean(), 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)
+
+ in_use = sa.Column('inUse', sa.Boolean(), nullable=True)
+
+ flags = sa.Column('numflag', sa.Integer(), nullable=True)
+
+ subdepartment_number = sa.Column('subdept', sa.SmallInteger(), nullable=True)
subdepartment = orm.relationship(
Subdepartment,
- primaryjoin='Subdepartment.number == Product.subdepartment_number',
- foreign_keys='Product.subdepartment_number',
+ primaryjoin=Subdepartment.number == subdepartment_number,
+ foreign_keys=[subdepartment_number],
doc="""
Reference to the :class:`Subdepartment` to which the product belongs.
""")
+ deposit = sa.Column(sa.Float(), nullable=True)
+
+ local = sa.Column(sa.Integer(), nullable=True)
+
+ store_id = sa.Column(sa.SmallInteger(), nullable=True)
+
+ default_vendor_id = sa.Column(sa.Integer(), nullable=True)
default_vendor = orm.relationship(
Vendor,
- primaryjoin='Vendor.id == Product.default_vendor_id',
- foreign_keys='Product.default_vendor_id',
+ primaryjoin=Vendor.id == default_vendor_id,
+ foreign_keys=[default_vendor_id],
doc="""
Reference to the default :class:`Vendor` from which the product is obtained.
""")
-
# TODO: deprecate / remove this?
vendor = orm.synonym('default_vendor')
+ current_origin_id = sa.Column(sa.Integer(), nullable=True)
+
+ # TODO: some older DB's might not have this? guess we'll see
+ last_sold = sa.Column(sa.DateTime(), nullable=True)
+
like_code = association_proxy(
'_like_code', 'like_code',
creator=lambda lc: ProductLikeCode(like_code=lc),
)
- vendor_items = orm.relationship(
- 'VendorItem',
- back_populates='product',
- primaryjoin='VendorItem.upc == Product.upc',
- foreign_keys='VendorItem.upc',
- order_by='VendorItem.vendor_item_id',
- doc="""
- List of :class:`VendorItem` records for this product.
- """)
-
- @property
- def default_vendor_item(self):
- """
- Returns the "default" vendor item record. This will
- correspond to the :attr:`default_vendor` if possible.
-
- :rtype: :class:`VendorItem` or ``None``
- """
- if self.default_vendor:
- for item in self.vendor_items:
- if item.vendor_id == self.default_vendor.id:
- return item
-
- if self.vendor_items:
- return self.vendor_items[0]
-
@property
def full_description(self):
fields = ['brand', 'description', 'size']
@@ -758,12 +813,17 @@ class VendorItem(Base):
upc = sa.Column(sa.String(length=13), nullable=False)
product = orm.relationship(
Product,
- back_populates='vendor_items',
primaryjoin=Product.upc == upc,
foreign_keys=[upc],
doc="""
Reference to the :class:`Product` to which this record applies.
- """)
+ """,
+ backref=orm.backref(
+ 'vendor_items',
+ order_by=vendor_item_id,
+ doc="""
+ List of :class:`VendorItem` records for this product.
+ """))
brand = sa.Column(sa.String(length=50), nullable=True)
@@ -918,12 +978,35 @@ class ProductPhysicalLocation(Base):
location = sa.Column(sa.SmallInteger(), nullable=True, default=0)
-class Employee(common.EmployeeBase, Base):
+class Employee(Base):
"""
- Data model for ``employees`` table.
+ Represents an employee within the organization.
"""
__tablename__ = 'employees'
+ number = sa.Column('emp_no', sa.SmallInteger(), primary_key=True, autoincrement=False, nullable=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 MemberType(Base):
"""
@@ -943,11 +1026,9 @@ class MemberType(Base):
ssi = sa.Column(sa.Boolean(), nullable=True)
- # nb. this must be added explicitly if DB is new enough
- #ignore_sales = sa.Column('ignoreSales', sa.Boolean(), nullable=True, default=False)
-
- # nb. this must be added explicitly if DB is new enough
- #sales_code = sa.Column('salesCode', sa.Integer(), nullable=True)
+ # TODO: this was apparently added "recently" - isn't present in all DBs
+ # (need to figure out how to conditionally include it in model?)
+ # sales_code = sa.Column('salesCode', sa.Integer(), nullable=True)
def __str__(self):
return self.description or ""
@@ -1073,7 +1154,7 @@ class Customer(Base):
return "{} {}".format(self.first_name or '', self.last_name or '').strip()
-class CustomerClassic(Base):
+class CustData(Base):
"""
Represents a customer of the organization.
@@ -1139,7 +1220,7 @@ class CustomerClassic(Base):
member_info = orm.relationship(
'MemberInfo',
- primaryjoin='MemberInfo.card_number == CustomerClassic.card_number',
+ primaryjoin='MemberInfo.card_number == CustData.card_number',
foreign_keys=[card_number],
uselist=False,
back_populates='customers',
@@ -1151,10 +1232,6 @@ class CustomerClassic(Base):
return "{} {}".format(self.first_name or '', self.last_name or '').strip()
-# TODO: deprecate / remove this
-CustData = CustomerClassic
-
-
class MemberInfo(Base):
"""
Contact info regarding a member of the organization.
@@ -1163,14 +1240,14 @@ class MemberInfo(Base):
card_number = sa.Column('card_no', sa.Integer(), primary_key=True, autoincrement=False, nullable=False)
- first_name = sa.Column(sa.String(length=30), nullable=True)
-
last_name = sa.Column(sa.String(length=30), nullable=True)
- other_first_name = sa.Column('othfirst_name', sa.String(length=30), nullable=True)
+ first_name = sa.Column(sa.String(length=30), nullable=True)
other_last_name = sa.Column('othlast_name', sa.String(length=30), nullable=True)
+ other_first_name = sa.Column('othfirst_name', sa.String(length=30), nullable=True)
+
street = sa.Column(sa.String(length=255), nullable=True)
city = sa.Column(sa.String(length=20), nullable=True)
@@ -1183,22 +1260,19 @@ class MemberInfo(Base):
email = sa.Column('email_1', sa.String(length=50), nullable=True)
- email2 = sa.Column('email_2', sa.String(length=50), nullable=True, doc="""
- NB. this is labeled "Alt. Phone" in CORE Office member view, and
- is named `altPhone` when dealing with CORE Office webservices API.
- """)
+ email2 = sa.Column('email_2', sa.String(length=50), nullable=True)
ads_ok = sa.Column('ads_OK', sa.Boolean(), nullable=True, default=True)
customers = orm.relationship(
- CustomerClassic,
- primaryjoin=CustomerClassic.card_number == card_number,
- order_by=CustomerClassic.person_number,
- foreign_keys=[CustomerClassic.card_number],
+ CustData,
+ primaryjoin=CustData.card_number == card_number,
+ order_by=CustData.person_number,
+ foreign_keys=[CustData.card_number],
back_populates='member_info',
- remote_side=CustomerClassic.card_number,
+ remote_side=CustData.card_number,
doc="""
- List of :class:`CustomerClassic` instances which are associated with this member info.
+ List of :class:`CustData` instances which are associated with this member info.
""")
dates = orm.relationship(
@@ -1206,7 +1280,6 @@ class MemberInfo(Base):
primaryjoin='MemberDate.card_number == MemberInfo.card_number',
foreign_keys='MemberDate.card_number',
cascade='all, delete-orphan',
- uselist=False,
doc="""
List of date records for the member.
""",
@@ -1309,7 +1382,7 @@ class MemberDate(Base):
class MemberContact(Base):
"""
- Member contacts
+ Contact preferences for members
"""
__tablename__ = 'memContact'
@@ -1336,19 +1409,6 @@ class MemberContact(Base):
return str(self.preference)
-class MemberContactPreference(Base):
- """
- Member contact preferences
- """
- __tablename__ = 'memContactPrefs'
-
- id = sa.Column('pref_id', sa.Integer(), primary_key=True, autoincrement=False, nullable=False)
- description = sa.Column('pref_description', sa.String(length=50), nullable=True)
-
- def __str__(self):
- return self.description or ""
-
-
class MemberBarcode(Base):
"""
Additional barcode for a member.
@@ -1384,26 +1444,6 @@ class MemberNote(Base):
return self.note or ""
-class CustomerNotification(Base):
- """
- Represents a customer notification for display at the lane.
-
- https://github.com/CORE-POS/IS4C/blob/master/fannie/classlib2.0/data/models/op/CustomerNotificationsModel.php
- """
- __tablename__ = 'CustomerNotifications'
-
- id = sa.Column('customerNotificationID', sa.Integer(), primary_key=True, autoincrement=True, nullable=False)
- card_number = sa.Column('cardNo', sa.Integer(), nullable=True)
- customer_id = sa.Column('customerID', sa.Integer(), nullable=True)
- source = sa.Column(sa.String(length=50), nullable=True)
- type = sa.Column(sa.String(length=50), nullable=True)
- message = sa.Column(sa.String(length=255), nullable=True)
- modifier_module = sa.Column('modifierModule', sa.String(length=50), nullable=True)
-
- def __str__(self):
- return self.message or ''
-
-
class ReasonCode(Base):
"""
Reason codes for legacy account suspensions.
@@ -1494,54 +1534,13 @@ class HouseCoupon(Base):
return self.description or ''
-class Tender(Base):
- """
- Represents a tender for payment at POS
- """
- __tablename__ = 'tenders'
-
- tender_id = sa.Column('TenderID', sa.Integer(), primary_key=True, nullable=False)
-
- tender_code = sa.Column('TenderCode', sa.String(length=2), nullable=True)
- tender_name = sa.Column('TenderName', sa.String(length=25), nullable=True)
- tender_type = sa.Column('TenderType', sa.String(length=2), nullable=True)
- change_message = sa.Column('ChangeMessage', sa.String(length=25), nullable=True)
- min_amount = sa.Column('MinAmount', sa.Numeric(precision=10, scale=2), nullable=True)
- max_amount = sa.Column('MaxAmount', sa.Numeric(precision=10, scale=2), nullable=True)
- max_refund = sa.Column('MaxRefund', sa.Numeric(precision=10, scale=2), nullable=True)
- tender_module = sa.Column('TenderModule', sa.String(length=50), nullable=True)
- sales_code = sa.Column('SalesCode', sa.Integer(), nullable=True)
-
- def __str__(self):
- return self.tender_name or ''
-
-
-class CustomReceiptLine(Base):
- """
- Represents a "text string" line for a custom receipt.
- """
- __tablename__ = 'customReceipt'
-
- # nb. this should be autoincrement, but we can't do that
- # automatically via sqlalchemy when PK is composite
- sequence = sa.Column('seq', sa.Integer(), primary_key=True, nullable=False)
-
- type = sa.Column(sa.String(length=20), primary_key=True, nullable=False)
- text = sa.Column(sa.String(length=80), nullable=True)
-
- def __str__(self):
- return self.text or ""
-
-
class BatchType(Base):
"""
Represents the definition of a batch type.
"""
__tablename__ = 'batchType'
- # nb. this is *not* autoincrement for some reason; must
- # calculate new ID manually based on max existing
- id = sa.Column('batchTypeID', sa.Integer(), nullable=False, primary_key=True, autoincrement=False)
+ id = sa.Column('batchTypeID', sa.Integer(), primary_key=True, autoincrement=False, nullable=False)
description = sa.Column('typeDesc', sa.String(length=50), nullable=True)
@@ -1551,7 +1550,7 @@ class BatchType(Base):
special_order_eligible = sa.Column('specialOrderEligible', sa.Boolean(), nullable=True, default=True)
- editor_ui = sa.Column('editorUI', sa.SmallInteger(), nullable=True, default=True)
+ editor_ui = sa.Column('editorUI', sa.Boolean(), nullable=True, default=True)
allow_single_store = sa.Column('allowSingleStore', sa.Boolean(), nullable=True, default=False)
@@ -1566,6 +1565,9 @@ class Batch(Base):
Represents a batch.
"""
__tablename__ = 'batches'
+ __table_args__ = (
+ sa.ForeignKeyConstraint(['batchType'], ['batchType.batchTypeID']),
+ )
id = sa.Column('batchID', sa.Integer(), primary_key=True, autoincrement=True, nullable=False)
@@ -1575,8 +1577,7 @@ class Batch(Base):
name = sa.Column('batchName', sa.String(length=80), nullable=True)
- batch_type_id = sa.Column('batchType', sa.Integer(),
- sa.ForeignKey('batchType.batchTypeID'), nullable=True)
+ batch_type_id = sa.Column('batchType', sa.Integer(), nullable=True)
batch_type = orm.relationship(BatchType)
discount_type = sa.Column('discountType', sa.Integer(), nullable=True)
@@ -1598,18 +1599,16 @@ class BatchItem(Base):
Represents a batch "list" item.
"""
__tablename__ = 'batchList'
+ __table_args__ = (
+ sa.ForeignKeyConstraint(['batchID'], ['batches.batchID']),
+ )
id = sa.Column('listID', sa.Integer(), primary_key=True, autoincrement=True, nullable=False)
- batch_id = sa.Column('batchID', sa.Integer(),
- sa.ForeignKey('batches.batchID'), nullable=True)
+ batch_id = sa.Column('batchID', sa.Integer(), nullable=True)
batch = orm.relationship(Batch, backref=orm.backref('items'))
upc = sa.Column(sa.String(length=13), nullable=True)
- product = orm.relationship(
- Product,
- primaryjoin=Product.upc == upc,
- foreign_keys=[upc])
sale_price = sa.Column('salePrice', sa.Numeric(precision=9, scale=3), nullable=True)
@@ -1745,22 +1744,3 @@ class PurchaseOrderNote(Base):
def __str__(self):
return self.notes or ""
-
-
-# the rest of this is a workaround to deal with the fact that some
-# CORE databases have columns which others do not. i had assumed that
-# all would be more or less the same but not so in practice. so if
-# your DB *does* have these columns, you must invoke the function
-# below in order to merge them into your schema. you should do this
-# on app startup and they'll be available normally from then on.
-
-RUNTIME = {'added_latest_columns': False}
-
-def use_latest_columns():
- if RUNTIME['added_latest_columns']:
- return
-
- MemberType.ignore_sales = sa.Column('ignoreSales', sa.Boolean(), nullable=True, default=False)
- MemberType.sales_code = sa.Column('salesCode', sa.Integer(), nullable=True)
-
- RUNTIME['added_latest_columns'] = True
diff --git a/corepos/db/office_trans/model.py b/corepos/db/office_trans/model.py
index c2b0959..7b6240c 100644
--- a/corepos/db/office_trans/model.py
+++ b/corepos/db/office_trans/model.py
@@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
-# Copyright © 2018-2025 Lance Edgar
+# Copyright © 2018-2020 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@@ -24,68 +24,108 @@
CORE POS Transaction Data Model
"""
+from __future__ import unicode_literals, absolute_import
+
+import six
import sqlalchemy as sa
-from sqlalchemy import orm
-
-from corepos.db.common import trans as common
+from sqlalchemy.ext.declarative import declarative_base
-Base = orm.declarative_base()
+Base = declarative_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):
+@six.python_2_unicode_compatible
+class TransactionDetailBase(object):
"""
- Represents a member equity payment.
+ Represents a POS transaction detail record.
"""
- __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
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)
+
+ # 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)
-class DTransaction(DTransactionBase, Base):
+ # 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 ''
+
+
+class TransactionDetail(TransactionDetailBase, Base):
"""
- Data model for ``dtransactions`` table.
+ Represents a POS transaction detail record.
"""
__tablename__ = 'dtransactions'
-
-
-# TODO: deprecate / remove this
-TransactionDetail = DTransaction
diff --git a/corepos/db/office_trans_archive/__init__.py b/corepos/db/office_trans_archive/__init__.py
index 6ddb31d..e25b200 100644
--- a/corepos/db/office_trans_archive/__init__.py
+++ b/corepos/db/office_trans_archive/__init__.py
@@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
-# Copyright © 2018-2023 Lance Edgar
+# Copyright © 2018-2022 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@@ -24,9 +24,7 @@
"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 sqlalchemy import orm
-from corepos.db.office_arch import *
+
+Session = orm.sessionmaker()
diff --git a/corepos/db/office_trans_archive/model.py b/corepos/db/office_trans_archive/model.py
index e57d071..ce70603 100644
--- a/corepos/db/office_trans_archive/model.py
+++ b/corepos/db/office_trans_archive/model.py
@@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
-# Copyright © 2018-2023 Lance Edgar
+# Copyright © 2018-2022 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@@ -24,9 +24,16 @@
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 sqlalchemy.ext.declarative import declarative_base
-from corepos.db.office_arch.model import *
+from corepos.db.office_trans.model import TransactionDetailBase
+
+
+Base = declarative_base()
+
+
+class TransactionDetail(TransactionDetailBase, Base):
+ """
+ Represents a POS transaction detail record.
+ """
+ __tablename__ = 'bigArchive'
diff --git a/corepos/enum.py b/corepos/enum.py
index 93780c3..55619d8 100644
--- a/corepos/enum.py
+++ b/corepos/enum.py
@@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
-# Copyright © 2018-2024 Lance Edgar
+# Copyright © 2018-2019 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@@ -24,43 +24,22 @@
CORE POS enumeration constants
"""
-from collections import OrderedDict
-from enum import Enum
+from __future__ import unicode_literals, absolute_import
+
+try:
+ from collections import OrderedDict
+except ImportError:
+ from ordereddict import OrderedDict
-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_PRICE_CHANGE, "Price Change"),
(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"),
+ (BATCH_DISCOUNT_TYPE_SALE_RESTRICTED, "Member/Owner only sale"),
])
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index cf50f41..0000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,49 +0,0 @@
-
-[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"]
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..098d0fc
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,97 @@
+# -*- 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 .
+#
+################################################################################
+
+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
+ 'requests', # 2.23.0
+ '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(),
+)
diff --git a/tasks.py b/tasks.py
index 6946a7f..a9b9fc3 100644
--- a/tasks.py
+++ b/tasks.py
@@ -2,7 +2,7 @@
################################################################################
#
# pyCOREPOS -- Python Interface to CORE POS
-# Copyright © 2018-2024 Lance Edgar
+# Copyright © 2018-2020 Lance Edgar
#
# This file is part of pyCOREPOS.
#
@@ -25,32 +25,20 @@ Tasks for 'pyCOREPOS' package
"""
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!")
+exec(open(os.path.join(here, 'corepos', '_version.py')).read())
@task
-def release(c):
+def release(ctx):
"""
Release a new version of 'pyCOREPOS'.
"""
- if os.path.exists('pyCOREPOS.egg-info'):
- shutil.rmtree('pyCOREPOS.egg-info')
- c.run('python -m build --sdist')
- c.run('twine upload dist/pycorepos-{}.tar.gz'.format(__version__))
+ shutil.rmtree('pyCOREPOS.egg-info')
+ ctx.run('python setup.py sdist --formats=gztar')
+ ctx.run('twine upload dist/pyCOREPOS-{}.tar.gz'.format(__version__))