Compare commits

..

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

26 changed files with 388 additions and 3065 deletions

3
.gitignore vendored
View file

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

View file

@ -5,177 +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/) The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
## v0.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 ## [0.1.0] - 2020-02-27
### Added ### Added
- Initial version of the package; defines some basic table mappings. - Initial version of the package; defines some basic table mappings.

View file

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

7
README.rst Normal file
View file

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

View file

@ -1,27 +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/>.
#
################################################################################
"""
CORE POS Interface
"""
from ._version import __version__

View file

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

View file

@ -1,578 +0,0 @@
# -*- 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 # pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2021 Lance Edgar # Copyright © 2018-2020 Lance Edgar
# #
# This file is part of pyCOREPOS. # This file is part of pyCOREPOS.
# #
@ -23,3 +23,12 @@
""" """
Database Interface 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

@ -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 <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)

View file

@ -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 <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

@ -1,30 +0,0 @@
# -*- 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()

View file

@ -1,167 +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 <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

@ -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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Lane Transaction Database
"""
from sqlalchemy import orm
Session = orm.sessionmaker()

View file

@ -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 <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

@ -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 <http://www.gnu.org/licenses/>.
#
################################################################################
"""
"Archive" Transaction Database Interface
"""
from sqlalchemy import orm
Session = orm.sessionmaker()

View file

@ -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 <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 # pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2025 Lance Edgar # Copyright © 2018-2020 Lance Edgar
# #
# This file is part of pyCOREPOS. # This file is part of pyCOREPOS.
# #
@ -24,68 +24,102 @@
CORE POS Transaction Data Model CORE POS Transaction Data Model
""" """
from __future__ import unicode_literals, absolute_import
import six
import sqlalchemy as sa import sqlalchemy as sa
from sqlalchemy import orm from sqlalchemy.ext.declarative import declarative_base
from corepos.db.common import trans as common
Base = orm.declarative_base() Base = declarative_base()
# TODO: not sure what primary key should be for this? am trying a @six.python_2_unicode_compatible
# composite one so far, we'll see...cf. also andy's comments in class TransactionDetail(Base):
# https://github.com/CORE-POS/IS4C/pull/1189#issuecomment-1597481138
class StockPurchase(Base):
""" """
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_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' __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)
# TODO: deprecate / remove this # register
TransactionDetail = DTransaction 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 ''

View file

@ -1,32 +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 <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

@ -1,32 +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 <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 # pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2020 Lance Edgar # Copyright © 2018-2019 Lance Edgar
# #
# This file is part of pyCOREPOS. # This file is part of pyCOREPOS.
# #
@ -24,9 +24,11 @@
CORE POS Database Utilities CORE POS Database Utilities
""" """
from __future__ import unicode_literals, absolute_import
import sqlalchemy as sa import sqlalchemy as sa
from corepos.db.office_op import model as corepos from corepos.db import model as corepos
def get_last_card_number(session): def get_last_card_number(session):
@ -36,25 +38,3 @@ def get_last_card_number(session):
""" """
return session.query(sa.func.max(corepos.Customer.card_number))\ return session.query(sa.func.max(corepos.Customer.card_number))\
.scalar() or 0 .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 # pyCOREPOS -- Python Interface to CORE POS
# Copyright © 2018-2024 Lance Edgar # Copyright © 2018-2019 Lance Edgar
# #
# This file is part of pyCOREPOS. # This file is part of pyCOREPOS.
# #
@ -24,44 +24,12 @@
CORE POS enumeration constants CORE POS enumeration constants
""" """
from collections import OrderedDict from __future__ import unicode_literals, absolute_import
from enum import Enum
try:
class CoreDbType(str, Enum): from collections import OrderedDict
office_op = 'office_op' except ImportError:
office_trans = 'office_trans' from ordereddict import OrderedDict
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 HOUSE_COUPON_MEMBER_ONLY_NO = 0
@ -124,14 +92,3 @@ MEMBER_CONTACT_PREFERENCE = OrderedDict([
(MEMBER_CONTACT_PREFERENCE_EMAIL_ONLY, "email only"), (MEMBER_CONTACT_PREFERENCE_EMAIL_ONLY, "email only"),
(MEMBER_CONTACT_PREFERENCE_BOTH, "both (postal mail and email)"), (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"),
])

View file

@ -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"]

96
setup.py Normal file
View file

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