Changeset - f6034c2c057d
[Not reviewed]
0 7 1
Lance Edgar (lance) - 3 years ago 2022-03-17 16:58:57
lance@edbob.org
Add custorder xref markers for trainwreck; import logic
8 files changed with 218 insertions and 7 deletions:
0 comments (0 inline, 0 general)
rattail/commands/importing.py
Show inline comments
 
@@ -78,17 +78,16 @@ class ImportSubcommand(Subcommand):
 

	
 
        if self.handler_key:
 
            handler = self.app.get_import_handler(self.handler_key)
 
            return type(handler)
 

	
 
        raise RuntimeError("Cannot locate a handler to use!  You "
 
                           "should probably define `{}.handler_key`"
 
                           "".format(self.__class__.__name__))
 
            if handler:
 
                return type(handler)
 

	
 
    def get_handler(self, **kwargs):
 
        """
 
        Returns a handler instance to be used by the command.
 
        """
 
        factory = self.get_handler_factory(args=kwargs.get('args'))
 
        if not factory:
 
            return
 
        kwargs.setdefault('config', getattr(self, 'config', None))
 
        kwargs.setdefault('command', self)
 
        kwargs.setdefault('progress', self.progress)
 
@@ -299,6 +298,11 @@ class ImportSubcommand(Subcommand):
 

	
 
    def list_all_models(self, args):
 
        handler = self.get_handler(args=args)
 
        if not handler:
 
            self.stderr.write("no handler configured!\n")
 
            if self.handler_key:
 
                self.stderr.write("handler key is: {}\n".format(self.handler_key))
 
            sys.exit(1)
 
        self.stdout.write("ALL MODELS:\n")
 
        self.stdout.write("==============================\n")
 
        defaults = handler.get_default_keys()
rattail/trainwreck/db/alembic/versions/c7022b294760_add_custorder_item_xref.py
Show inline comments
 
new file 100644
 
# -*- coding: utf-8; -*-
 
"""add custorder_item_xref
 

	
 
Revision ID: c7022b294760
 
Revises: fae21d8c854a
 
Create Date: 2022-03-16 22:02:04.370686
 

	
 
"""
 

	
 
from __future__ import unicode_literals, absolute_import
 

	
 
from alembic import op
 
import sqlalchemy as sa
 

	
 

	
 
# revision identifiers, used by Alembic.
 
revision = 'c7022b294760'
 
down_revision = 'fae21d8c854a'
 
branch_labels = None
 
depends_on = None
 

	
 

	
 
def upgrade():
 

	
 
    # transaction_order_marker
 
    op.create_table('transaction_order_marker',
 
                    sa.Column('uuid', sa.String(length=32), nullable=False),
 
                    sa.Column('transaction_uuid', sa.String(length=32), nullable=False),
 
                    sa.Column('custorder_xref', sa.String(length=50), nullable=True),
 
                    sa.Column('custorder_item_xref', sa.String(length=50), nullable=True),
 
                    sa.ForeignKeyConstraint(['transaction_uuid'], ['transaction.uuid'], name='transaction_order_marker_fk_transaction'),
 
                    sa.PrimaryKeyConstraint('uuid')
 
    )
 

	
 
    # transaction_item
 
    op.add_column('transaction_item', sa.Column('custorder_item_xref', sa.String(length=50), nullable=True))
 

	
 

	
 
def downgrade():
 

	
 
    # transaction_item
 
    op.drop_column('transaction_item', 'custorder_item_xref')
 

	
 
    # transaction_order_marker
 
    op.drop_table('transaction_order_marker')
rattail/trainwreck/db/model/__init__.py
Show inline comments
 
@@ -28,5 +28,6 @@ from __future__ import unicode_literals, absolute_import
 

	
 
from .base import (Base, uuid_column,
 
                   TransactionBase,
 
                   TransactionOrderMarkerBase,
 
                   TransactionItemBase,
 
                   TransactionItemDiscountBase)
rattail/trainwreck/db/model/base.py
Show inline comments
 
@@ -197,6 +197,75 @@ class TransactionBase(object):
 
        return "{} {}-{}".format(system, self.terminal_id or "?", self.receipt_number or "??")
 

	
 

	
 
@six.python_2_unicode_compatible
 
class TransactionOrderMarkerBase(object):
 
    """
 
    Represents a "marker" to cross-reference one or more customer
 
    orders with a given POS transaction.
 
    """
 
    __tablename__ = 'transaction_order_marker'
 

	
 
    @declared_attr
 
    def __table_args__(cls):
 
        return cls.__txnmarker_table_args__()
 

	
 
    @classmethod
 
    def __txnmarker_table_args__(cls):
 
        txn_table = cls.__txn_class__.__tablename__
 
        marker_table = cls.__tablename__
 
        return (
 
            sa.ForeignKeyConstraint(['transaction_uuid'], ['{}.uuid'.format(txn_table)],
 
                                    name='{}_fk_transaction'.format(marker_table)),
 
        )
 

	
 
    uuid = uuid_column()
 

	
 
    transaction_uuid = sa.Column(sa.String(length=32), nullable=False)
 

	
 
    @declared_attr
 
    def transaction(cls):
 
        txn_class = cls.__txn_class__
 
        marker_class = cls
 
        # txn_class.order_xref_marker_class = marker_class
 

	
 
        # Must establish `Transaction.items` here instead of from within
 
        # Transaction itself, because the item class doesn't yet exist (really,
 
        # it can't even be "known") when Transaction is being created.
 
        txn_class.custorder_xref_markers = orm.relationship(
 
            marker_class,
 
            cascade='all, delete-orphan',
 
            doc="""
 
            Set of "order xref markers" for the transaction.
 
            """,
 
            back_populates='transaction')
 

	
 
        # Now, here's the `TransactionOrderMarker.transaction` reference.
 
        return orm.relationship(
 
            txn_class,
 
            back_populates='custorder_xref_markers',
 
            doc="""
 
            Reference to the order maker's parent transaction.
 
            """)
 

	
 
    custorder_xref = sa.Column(sa.String(length=50), nullable=True, doc="""
 
    Cross-reference identifier for a "customer order" which
 
    corresponds to this transaction, if applicable.  More than one
 
    such order xref can exist for a transaction.
 
    """)
 

	
 
    custorder_item_xref = sa.Column(sa.String(length=50), nullable=True, doc="""
 
    Cross-reference identifier for a "customer order item" which
 
    corresponds to this transaction, if applicable.  More than one
 
    such order xref can exist for a transaction.
 
    """)
 

	
 
    def __str__(self):
 
        xref = self.custorder_xref or "??"
 
        if self.custorder_item_xref:
 
            xref = "{} / {}".format(xref, self.custorder_item_xref)
 
        return "{} / {}".format(self.transaction, xref)
 

	
 

	
 
@six.python_2_unicode_compatible
 
class TransactionItemBase(object):
 
    """
 
@@ -265,6 +334,12 @@ class TransactionItemBase(object):
 
    scancode, for systems where multiple items may have the same scancode.
 
    """)
 

	
 
    custorder_item_xref = sa.Column(sa.String(length=50), nullable=True, doc="""
 
    Cross-reference identifier for the "customer order item" which
 
    corresponds to this line item, if applicable.  Can be useful to
 
    tie these things together for sake of reporting.
 
    """)
 

	
 
    department_number = sa.Column(sa.Integer(), nullable=True, doc="""
 
    Department number for the line item, if known.
 
    """)
rattail/trainwreck/db/model/defaults.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2021 Lance Edgar
 
#  Copyright © 2010-2022 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -40,6 +40,13 @@ class Transaction(model.TransactionBase, model.Base):
 
    """
 

	
 

	
 
class TransactionOrderMarker(model.TransactionOrderMarkerBase, model.Base):
 
    """
 
    Represents a "customer order xref" for a transaction.
 
    """
 
    __txn_class__ = Transaction
 

	
 

	
 
class TransactionItem(model.TransactionItemBase, model.Base):
 
    """
 
    Represents a line item within a transaction.
rattail/trainwreck/importing/local.py
Show inline comments
 
@@ -68,6 +68,7 @@ class TransactionImporter(FromTrainwreck, trainwreck_importing.model.Transaction
 
    Base class for Trainwreck -> "self" for Transaction model.
 
    """
 
    allow_create = False
 
    allow_delete = False
 

	
 
    @property
 
    def host_model_class(self):
 
@@ -86,3 +87,14 @@ class TransactionImporter(FromTrainwreck, trainwreck_importing.model.Transaction
 
                              .joinedload(trainwreck.TransactionItem.discounts))
 

	
 
        return query
 

	
 
    def normalize_host_object(self, txn):
 
        """
 
        This method is defined only as a convenience for the simple
 
        case where you need to mark a transaction as having been
 
        updated, along with some other "processing" logic.
 
        """
 
        return {
 
            'uuid': txn.uuid,
 
            'self_updated': True,
 
        }
rattail/trainwreck/importing/model.py
Show inline comments
 
@@ -82,6 +82,55 @@ class TransactionImporter(ToTrainwreck):
 
                    .filter(time_field < self.app.make_utc(self.end_time))
 

	
 

	
 
class TransactionOrderMarkerImporter(ToTrainwreck):
 
    """
 
    Transaction order marker data importer
 

	
 
    .. attribute:: match_on_time_field
 

	
 
       This time field will be used for the cache query etc.  In
 
       particular it controls which transactions are deemed to belong
 
       to a given date, which is needed when restricting the import to
 
       a particular date range.  Can set this to ``'upload_time'`` or
 
       ``'start_time'`` if necessary, depending on the nature of
 
       transaction data coming from the host side.
 
    """
 
    match_on_time_field = 'end_time'
 

	
 
    @property
 
    def importing_from_system(self):
 
        raise NotImplementedError("TODO: please define this for your subclass")
 

	
 
    def get_model_class(self):
 
        if hasattr(self, 'model_class') and self.model_class:
 
            return self.model_class
 
        trainwreck = self.config.get_trainwreck_model()
 
        return trainwreck.TransactionOrderMarker
 

	
 
    @property
 
    def transaction_class(self):
 
        trainwreck = self.config.get_trainwreck_model()
 
        return trainwreck.Transaction
 

	
 
    def cache_query(self):
 
        query = super(TransactionOrderMarkerImporter, self).cache_query()
 
        trainwreck = self.config.get_trainwreck_model()
 

	
 
        query = query.join(trainwreck.Transaction)
 

	
 
        try:
 
            system = self.importing_from_system
 
        except NotImplementedError:
 
            pass
 
        else:
 
            query = query.filter(trainwreck.Transaction.system == system)
 

	
 
        time_field = getattr(trainwreck.Transaction, self.match_on_time_field)
 
        query = query.filter(time_field >= self.app.make_utc(self.start_time))\
 
                     .filter(time_field < self.app.make_utc(self.end_time))
 
        return query
 

	
 

	
 
class TransactionItemImporter(ToTrainwreck):
 
    """
 
    Transaction item data importer
rattail/trainwreck/importing/trainwreck.py
Show inline comments
 
@@ -2,7 +2,7 @@
 
################################################################################
 
#
 
#  Rattail -- Retail Software Framework
 
#  Copyright © 2010-2021 Lance Edgar
 
#  Copyright © 2010-2022 Lance Edgar
 
#
 
#  This file is part of Rattail.
 
#
 
@@ -69,6 +69,7 @@ class TrainwreckImportExportBase(FromTrainwreckHandler, ToTrainwreckHandler):
 
    def get_importers(self):
 
        importers = OrderedDict()
 
        importers['Transaction'] = TransactionImporter
 
        importers['TransactionOrderMarker'] = TransactionOrderMarkerImporter
 
        importers['TransactionItem'] = TransactionItemImporter
 
        importers['TransactionItemDiscount'] = TransactionItemDiscountImporter
 
        return importers
 
@@ -135,6 +136,23 @@ class TransactionImporter(FromTrainwreck, model.TransactionImporter):
 
                    .filter(self.model_class.end_time < make_utc(self.end_time))
 

	
 

	
 
class TransactionOrderMarkerImporter(FromTrainwreck, model.TransactionOrderMarkerImporter):
 
    """
 
    Base class for Transaction order marker data importer
 
    """
 

	
 
    def query(self):
 
        query = super(TransactionOrderMarkerImporter, self).query()
 
        query = self.filter_date_range(query)
 
        return query
 

	
 
    def filter_date_range(self, query):
 
        time_field = getattr(self.transaction_class, self.match_on_time_field)
 
        return query.join(self.transaction_class)\
 
                    .filter(time_field >= self.app.make_utc(self.start_time))\
 
                    .filter(time_field < self.app.make_utc(self.end_time))
 

	
 

	
 
class TransactionItemImporter(FromTrainwreck, model.TransactionItemImporter):
 
    """
 
    Base class for Transaction item data importer
0 comments (0 inline, 0 general)