Add employee importer for CORE -> Rattail, and CORE cashier auth handler
This commit is contained in:
parent
fd5d3142ed
commit
117442f8db
|
@ -92,6 +92,21 @@ class CoreHandler(GenericHandler):
|
|||
# TODO: deprecate / remove this
|
||||
get_office_customer_account_url = get_office_member_url
|
||||
|
||||
def get_office_employee_url(
|
||||
self,
|
||||
employee_number,
|
||||
office_url=None,
|
||||
require=False,
|
||||
**kwargs):
|
||||
"""
|
||||
Returns the CORE Office URL for the employee with the given
|
||||
number.
|
||||
"""
|
||||
if not office_url:
|
||||
office_url = self.get_office_url(require=require)
|
||||
if office_url:
|
||||
return f'{office_url}/admin/Cashiers/CashierEditor.php?emp_no={employee_number}'
|
||||
|
||||
def get_office_product_url(
|
||||
self,
|
||||
upc,
|
||||
|
|
80
rattail_corepos/auth.py
Normal file
80
rattail_corepos/auth.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
# Rattail 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.
|
||||
#
|
||||
# Rattail 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
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
Auth Handler for use with CORE-POS
|
||||
"""
|
||||
|
||||
from sqlalchemy import orm
|
||||
|
||||
from rattail import auth as base
|
||||
|
||||
|
||||
class CoreAuthHandler(base.AuthHandler):
|
||||
"""
|
||||
Custom auth handler for use with CORE-POS.
|
||||
"""
|
||||
|
||||
def authenticate_user(self, session, username, password):
|
||||
model = self.model
|
||||
|
||||
# first try default logic, if it works then great
|
||||
user = super().authenticate_user(session, username, password)
|
||||
if user:
|
||||
return user
|
||||
|
||||
# only if configured, we also check CORE-POS credentials
|
||||
if self.config.getbool('rattail.auth', 'corepos.check_cashier_credentials',
|
||||
default=False):
|
||||
user = None
|
||||
core_session = self.app.get_corepos_handler().make_session_office_op()
|
||||
core_employee = self.check_corepos_cashier_credentials(core_session, password)
|
||||
if core_employee:
|
||||
user = self.get_user_from_corepos_employee(session, core_employee)
|
||||
core_session.close()
|
||||
if user and user.active:
|
||||
return user
|
||||
|
||||
def check_corepos_cashier_credentials(self, core_session, password):
|
||||
core_op = self.app.get_corepos_handler().get_model_office_op()
|
||||
|
||||
try:
|
||||
core_employee = core_session.query(core_op.Employee)\
|
||||
.filter(core_op.Employee.cashier_password == password)\
|
||||
.one()
|
||||
except orm.exc.NoResultFound:
|
||||
pass
|
||||
else:
|
||||
if core_employee.active:
|
||||
return core_employee
|
||||
|
||||
def get_user_from_corepos_employee(self, session, core_employee):
|
||||
model = self.model
|
||||
try:
|
||||
employee = session.query(model.Employee)\
|
||||
.join(model.CoreEmployee)\
|
||||
.filter(model.CoreEmployee.corepos_number == core_employee.number)\
|
||||
.one()
|
||||
except orm.exc.NoResultFound:
|
||||
pass
|
||||
else:
|
||||
return self.app.get_user(employee)
|
|
@ -0,0 +1,51 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
"""add core_employee
|
||||
|
||||
Revision ID: 1f2e2f57c90b
|
||||
Revises: c40a6ec00428
|
||||
Create Date: 2023-10-01 19:03:56.921897
|
||||
|
||||
"""
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '1f2e2f57c90b'
|
||||
down_revision = 'c40a6ec00428'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
import rattail.db.types
|
||||
|
||||
|
||||
|
||||
def upgrade():
|
||||
|
||||
# corepos_employee
|
||||
op.create_table('corepos_employee',
|
||||
sa.Column('uuid', sa.String(length=32), nullable=False),
|
||||
sa.Column('corepos_number', sa.Integer(), nullable=True),
|
||||
sa.ForeignKeyConstraint(['uuid'], ['employee.uuid'], name='corepos_employee_fk_employee'),
|
||||
sa.PrimaryKeyConstraint('uuid')
|
||||
)
|
||||
op.create_table('corepos_employee_version',
|
||||
sa.Column('uuid', sa.String(length=32), autoincrement=False, nullable=False),
|
||||
sa.Column('corepos_number', sa.Integer(), autoincrement=False, nullable=True),
|
||||
sa.Column('transaction_id', sa.BigInteger(), autoincrement=False, nullable=False),
|
||||
sa.Column('end_transaction_id', sa.BigInteger(), nullable=True),
|
||||
sa.Column('operation_type', sa.SmallInteger(), nullable=False),
|
||||
sa.PrimaryKeyConstraint('uuid', 'transaction_id')
|
||||
)
|
||||
op.create_index(op.f('ix_corepos_employee_version_end_transaction_id'), 'corepos_employee_version', ['end_transaction_id'], unique=False)
|
||||
op.create_index(op.f('ix_corepos_employee_version_operation_type'), 'corepos_employee_version', ['operation_type'], unique=False)
|
||||
op.create_index(op.f('ix_corepos_employee_version_transaction_id'), 'corepos_employee_version', ['transaction_id'], unique=False)
|
||||
|
||||
|
||||
def downgrade():
|
||||
|
||||
# corepos_employee
|
||||
op.drop_index(op.f('ix_corepos_employee_version_transaction_id'), table_name='corepos_employee_version')
|
||||
op.drop_index(op.f('ix_corepos_employee_version_operation_type'), table_name='corepos_employee_version')
|
||||
op.drop_index(op.f('ix_corepos_employee_version_end_transaction_id'), table_name='corepos_employee_version')
|
||||
op.drop_table('corepos_employee_version')
|
||||
op.drop_table('corepos_employee')
|
|
@ -25,7 +25,8 @@ Database schema extensions for CORE-POS integration
|
|||
"""
|
||||
|
||||
from .stores import CoreStore, CoreTender
|
||||
from .people import (CorePerson, CoreCustomer, CoreCustomerShopper,
|
||||
from .people import (CorePerson, CoreEmployee,
|
||||
CoreCustomer, CoreCustomerShopper,
|
||||
CoreMember, CoreMemberEquityPayment)
|
||||
from .products import (CoreDepartment, CoreSubdepartment,
|
||||
CoreVendor, CoreProduct, CoreProductCost)
|
||||
|
|
28
rattail_corepos/db/model/_complete.py
Normal file
28
rattail_corepos/db/model/_complete.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# -*- coding: utf-8; -*-
|
||||
################################################################################
|
||||
#
|
||||
# Rattail -- Retail Software Framework
|
||||
# Copyright © 2010-2023 Lance Edgar
|
||||
#
|
||||
# This file is part of Rattail.
|
||||
#
|
||||
# Rattail 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.
|
||||
#
|
||||
# Rattail 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
|
||||
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
################################################################################
|
||||
"""
|
||||
A "complete" data model including CORE-POS integration
|
||||
"""
|
||||
|
||||
from rattail.db.model import *
|
||||
from rattail_corepos.db.model import *
|
|
@ -65,6 +65,41 @@ class CorePerson(model.Base):
|
|||
CorePerson.make_proxy(model.Person, '_corepos', 'corepos_customer_id')
|
||||
|
||||
|
||||
class CoreEmployee(model.Base):
|
||||
"""
|
||||
CORE-specific extensions to :class:`~rattail:rattail.db.model.Employee`
|
||||
"""
|
||||
__tablename__ = 'corepos_employee'
|
||||
__table_args__ = (
|
||||
sa.ForeignKeyConstraint(['uuid'], ['employee.uuid'],
|
||||
name='corepos_employee_fk_employee'),
|
||||
)
|
||||
__versioned__ = {}
|
||||
|
||||
uuid = model.uuid_column(default=None)
|
||||
employee = orm.relationship(
|
||||
model.Employee,
|
||||
doc="""
|
||||
Reference to the actual employee record, which this one extends.
|
||||
""",
|
||||
backref=orm.backref(
|
||||
'_corepos',
|
||||
uselist=False,
|
||||
cascade='all, delete-orphan',
|
||||
doc="""
|
||||
Reference to the CORE-POS extension record for this employee.
|
||||
"""))
|
||||
|
||||
corepos_number = sa.Column(sa.Integer(), nullable=True, doc="""
|
||||
``employees.emp_no`` value for this employee, within CORE-POS.
|
||||
""")
|
||||
|
||||
def __str__(self):
|
||||
return str(self.employee)
|
||||
|
||||
CoreEmployee.make_proxy(model.Employee, '_corepos', 'corepos_number')
|
||||
|
||||
|
||||
class CoreCustomer(model.Base):
|
||||
"""
|
||||
CORE-specific extensions to :class:`rattail:rattail.db.model.Customer`.
|
||||
|
|
|
@ -35,6 +35,7 @@ from corepos.db.office_trans import model as coretrans, Session as CoreTransSess
|
|||
|
||||
from rattail import importing
|
||||
from rattail.gpc import GPC
|
||||
from rattail.db.util import normalize_full_name
|
||||
from rattail_corepos import importing as corepos_importing
|
||||
|
||||
|
||||
|
@ -65,6 +66,7 @@ class FromCOREPOSToRattail(importing.FromSQLAlchemyHandler, importing.ToRattailH
|
|||
|
||||
def get_importers(self):
|
||||
importers = OrderedDict()
|
||||
importers['Employee'] = EmployeeImporter
|
||||
importers['Tender'] = TenderImporter
|
||||
importers['Vendor'] = VendorImporter
|
||||
importers['Department'] = DepartmentImporter
|
||||
|
@ -88,6 +90,30 @@ class FromCOREPOS(importing.FromSQLAlchemy):
|
|||
"""
|
||||
|
||||
|
||||
class EmployeeImporter(FromCOREPOS, corepos_importing.model.EmployeeImporter):
|
||||
"""
|
||||
Importer for employee data from CORE POS.
|
||||
"""
|
||||
host_model_class = corepos.Employee
|
||||
key = 'corepos_number'
|
||||
supported_fields = [
|
||||
'corepos_number',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'full_name',
|
||||
'status',
|
||||
]
|
||||
|
||||
def normalize_host_object(self, employee):
|
||||
return {
|
||||
'corepos_number': employee.number,
|
||||
'first_name': employee.first_name,
|
||||
'last_name': employee.last_name,
|
||||
'full_name': normalize_full_name(employee.first_name, employee.last_name),
|
||||
'status': self.enum.EMPLOYEE_STATUS_CURRENT if employee.active else self.enum.EMPLOYEE_STATUS_FORMER,
|
||||
}
|
||||
|
||||
|
||||
class TenderImporter(FromCOREPOS, corepos_importing.model.TenderImporter):
|
||||
"""
|
||||
Importer for tender data from CORE POS.
|
||||
|
|
|
@ -54,6 +54,15 @@ class PersonImporter(importing.model.PersonImporter):
|
|||
return query
|
||||
|
||||
|
||||
class EmployeeImporter(importing.model.EmployeeImporter):
|
||||
|
||||
extensions = {
|
||||
'_corepos': [
|
||||
'corepos_number',
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
class CustomerImporter(importing.model.CustomerImporter):
|
||||
|
||||
extensions = {
|
||||
|
|
|
@ -34,6 +34,7 @@ class CoreposVersionMixin(object):
|
|||
|
||||
def add_corepos_importers(self, importers):
|
||||
importers['CorePerson'] = CorePersonImporter
|
||||
importers['CoreEmployee'] = CoreEmployeeImporter
|
||||
importers['CoreCustomer'] = CoreCustomerImporter
|
||||
importers['CoreCustomerShopper'] = CoreCustomerShopperImporter
|
||||
importers['CoreMember'] = CoreMemberImporter
|
||||
|
@ -54,6 +55,14 @@ class CorePersonImporter(base.VersionImporter):
|
|||
return model.CorePerson
|
||||
|
||||
|
||||
class CoreEmployeeImporter(base.VersionImporter):
|
||||
|
||||
@property
|
||||
def host_model_class(self):
|
||||
model = self.config.get_model()
|
||||
return model.CoreEmployee
|
||||
|
||||
|
||||
class CoreCustomerImporter(base.VersionImporter):
|
||||
|
||||
@property
|
||||
|
|
Loading…
Reference in a new issue