From 2fe089bd57b93388908a7b5adff3709ebde2e0fb Mon Sep 17 00:00:00 2001
From: Lance Edgar <lance@wuttaproject.org>
Date: Sat, 25 Jan 2025 16:41:17 -0600
Subject: [PATCH 1/3] fix: add `Parameter` model for lane_op

---
 corepos/db/common/op.py       | 18 ++++++++++++++++++
 corepos/db/lane_op/model.py   |  7 +++++++
 corepos/db/office_op/model.py | 17 ++---------------
 3 files changed, 27 insertions(+), 15 deletions(-)

diff --git a/corepos/db/common/op.py b/corepos/db/common/op.py
index 7cf84fc..c0d40f5 100644
--- a/corepos/db/common/op.py
+++ b/corepos/db/common/op.py
@@ -27,6 +27,24 @@ 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.
diff --git a/corepos/db/lane_op/model.py b/corepos/db/lane_op/model.py
index 08da4f2..3074dd1 100644
--- a/corepos/db/lane_op/model.py
+++ b/corepos/db/lane_op/model.py
@@ -33,6 +33,13 @@ 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.
diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py
index 67f405a..ad4b89b 100644
--- a/corepos/db/office_op/model.py
+++ b/corepos/db/office_op/model.py
@@ -56,25 +56,12 @@ class StringableDateTime(sa.TypeDecorator):
         raise NotImplementedError
 
 
-class Parameter(Base):
+class Parameter(common.ParameterBase, Base):
     """
-    Represents a "parameter" value.
+    Data model for ``parameters`` table.
     """
     __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):
     """

From a2a1d7faee837c0a0216693a635fe5f237ce6366 Mon Sep 17 00:00:00 2001
From: Lance Edgar <lance@wuttaproject.org>
Date: Sat, 25 Jan 2025 16:58:17 -0600
Subject: [PATCH 2/3] fix: define common base schema for Product model

---
 corepos/db/common/op.py       |  99 +++++++++++++++++++++++++
 corepos/db/lane_op/model.py   | 134 +---------------------------------
 corepos/db/office_op/model.py |  99 ++-----------------------
 3 files changed, 109 insertions(+), 223 deletions(-)

diff --git a/corepos/db/common/op.py b/corepos/db/common/op.py
index c0d40f5..30ecc1c 100644
--- a/corepos/db/common/op.py
+++ b/corepos/db/common/op.py
@@ -72,3 +72,102 @@ class EmployeeBase:
 
     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/lane_op/model.py b/corepos/db/lane_op/model.py
index 3074dd1..456b1b8 100644
--- a/corepos/db/lane_op/model.py
+++ b/corepos/db/lane_op/model.py
@@ -88,141 +88,11 @@ class Department(Base):
         return self.name or ""
 
 
-class Product(Base):
+class Product(common.ProductBase, Base):
     """
-    Represents a product, purchased and/or sold by the organization.
+    Data model for ``products`` table.
     """
     __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):
diff --git a/corepos/db/office_op/model.py b/corepos/db/office_op/model.py
index ad4b89b..e374d37 100644
--- a/corepos/db/office_op/model.py
+++ b/corepos/db/office_op/model.py
@@ -536,7 +536,7 @@ class Origin(Base):
         return self.name or self.short_name or ""
 
 
-class Product(Base):
+class Product(common.ProductBase, Base):
     """
     Represents a product, purchased and/or sold by the organization.
     """
@@ -547,120 +547,37 @@ class Product(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 == department_number,
-        foreign_keys=[department_number],
+        primaryjoin='Department.number == Product.department_number',
+        foreign_keys='Product.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 == subdepartment_number,
-        foreign_keys=[subdepartment_number],
+        primaryjoin='Subdepartment.number == Product.subdepartment_number',
+        foreign_keys='Product.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 == default_vendor_id,
-        foreign_keys=[default_vendor_id],
+        primaryjoin='Vendor.id == Product.default_vendor_id',
+        foreign_keys='Product.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),

From 97a1396a543e052caf9bd24c5ad1837f9e68eb39 Mon Sep 17 00:00:00 2001
From: Lance Edgar <lance@wuttaproject.org>
Date: Sat, 25 Jan 2025 17:01:10 -0600
Subject: [PATCH 3/3] feat: use true column names for transaction data models

as much as i kind of want to "rename" some of these for convenience,
it seems safest here to just stick with true names to avoid confusion
---
 corepos/db/common/trans.py | 59 ++++++++++++++++----------------------
 1 file changed, 25 insertions(+), 34 deletions(-)

diff --git a/corepos/db/common/trans.py b/corepos/db/common/trans.py
index 2b4e503..9ec5601 100644
--- a/corepos/db/common/trans.py
+++ b/corepos/db/common/trans.py
@@ -36,25 +36,21 @@ class TransactionDetailBase:
     """
 
     # register
-    register_number = sa.Column('register_no', sa.Integer(), nullable=True)
+    register_no = 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)
+    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)
 
-    @declared_attr
-    def transaction_status(self):
-        return orm.synonym('trans_status')
-
     # cashier
-    employee_number = sa.Column('emp_no', sa.Integer(), nullable=True)
+    emp_no = sa.Column(sa.Integer(), nullable=True)
 
     # customer
-    card_number = sa.Column('card_no', sa.Integer(), nullable=True)
-    member_type = sa.Column('memType', sa.Integer(), nullable=True)
+    card_no = sa.Column(sa.Integer(), nullable=True)
+    memType = sa.Column(sa.Integer(), nullable=True)
     staff = sa.Column(sa.Boolean(), nullable=True)
 
     ##############################
@@ -63,7 +59,7 @@ class TransactionDetailBase:
 
     upc = sa.Column(sa.String(length=13), nullable=True)
 
-    department_number = sa.Column('department', sa.Integer(), nullable=True)
+    department = sa.Column(sa.Integer(), nullable=True)
 
     description = sa.Column(sa.String(length=30), nullable=True)
 
@@ -73,51 +69,46 @@ class TransactionDetailBase:
 
     cost = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
 
-    unitPrice = sa.Column('unitPrice', sa.Numeric(precision=10, scale=2), nullable=True)
-
-    @declared_attr
-    def unit_price(self):
-        return orm.synonym('unitPrice')
+    unitPrice = sa.Column(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)
+    regPrice = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
 
     tax = sa.Column(sa.SmallInteger(), nullable=True)
 
-    @declared_attr
-    def tax_rate_id(self):
-        return orm.synonym('tax')
-
-    food_stamp = sa.Column('foodstamp', sa.Boolean(), nullable=True)
+    foodstamp = sa.Column(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)
+    memDiscount = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
 
     discountable = sa.Column(sa.Boolean(), nullable=True)
 
-    discount_type = sa.Column('discounttype', sa.Integer(), nullable=True)
+    discounttype = sa.Column(sa.Integer(), nullable=True)
 
     voided = sa.Column(sa.Integer(), nullable=True)
 
-    percent_discount = sa.Column('percentDiscount', sa.Integer(), nullable=True)
+    percentDiscount = sa.Column(sa.Integer(), nullable=True)
 
-    item_quantity = sa.Column('ItemQtty', sa.Float(), nullable=True)
+    ItemQtty = sa.Column(sa.Float(), nullable=True)
 
-    volume_discount_type = sa.Column('volDiscType', sa.Integer(), nullable=True)
+    volDiscType = sa.Column(sa.Integer(), nullable=True)
 
     volume = sa.Column(sa.Integer(), nullable=True)
 
-    volume_special = sa.Column('VolSpecial', sa.Numeric(precision=10, scale=2), nullable=True)
+    VolSpecial = sa.Column(sa.Numeric(precision=10, scale=2), nullable=True)
 
-    mix_match = sa.Column('mixMatch', sa.String(length=13), nullable=True)
+    mixMatch = sa.Column(sa.String(length=13), nullable=True)
 
     matched = sa.Column(sa.Boolean(), nullable=True)
 
-    num_flag = sa.Column('numflag', sa.Integer(), nullable=True, default=0)
+    numflag = sa.Column(sa.Integer(), nullable=True, default=0)
 
-    char_flag = sa.Column('charflag', sa.String(length=2), nullable=True)
+    charflag = sa.Column(sa.String(length=2), nullable=True)
 
     def __str__(self):
-        return self.description or ''
+        txnid = '-'.join([str(val) for val in [self.register_no,
+                                               self.trans_no,
+                                               self.trans_id]])
+        return f"{txnid} {self.description or ''}"