Add data models for "customer order" batches

This commit is contained in:
Lance Edgar 2020-08-02 15:26:48 -05:00
parent b44f45b8fd
commit 42d20a7c0b
6 changed files with 324 additions and 9 deletions

View file

@ -0,0 +1,40 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2020 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/>.
#
################################################################################
"""
Handler for "customer order" batches
"""
from __future__ import unicode_literals, absolute_import, division
from rattail.db import model
from rattail.batch import BatchHandler
class CustomerOrderBatchHandler(BatchHandler):
"""
Handler for all "customer order" batches, regardless of "mode". The
handler must inspect the
:attr:`~rattail.db.model.batch.custorder.CustomerOrderBatch.mode` attribute
of each batch it deals with, in order to determine which logic to apply.
"""
batch_model_class = model.CustomerOrderBatch

View file

@ -0,0 +1,116 @@
# -*- coding: utf-8; -*-
"""add custorder_batch
Revision ID: cb80a199df02
Revises: aeea3c6fc6af
Create Date: 2020-08-02 01:15:56.242415
"""
from __future__ import unicode_literals
# revision identifiers, used by Alembic.
revision = 'cb80a199df02'
down_revision = 'aeea3c6fc6af'
branch_labels = None
depends_on = None
from alembic import op
import sqlalchemy as sa
import rattail.db.types
def upgrade():
# custorder
op.add_column('custorder', sa.Column('email_address', sa.String(length=255), nullable=True))
op.add_column('custorder', sa.Column('phone_number', sa.String(length=20), nullable=True))
op.add_column('custorder', sa.Column('created_by_uuid', sa.String(length=32), nullable=True))
op.add_column('custorder', sa.Column('store_uuid', sa.String(length=32), nullable=True))
op.create_foreign_key('custorder_fk_created_by', 'custorder', 'user', ['created_by_uuid'], ['uuid'])
op.create_foreign_key('custorder_fk_store', 'custorder', 'store', ['store_uuid'], ['uuid'])
# custorder_batch
op.create_table('custorder_batch',
sa.Column('store_uuid', sa.String(length=32), nullable=True),
sa.Column('customer_uuid', sa.String(length=32), nullable=True),
sa.Column('person_uuid', sa.String(length=32), nullable=True),
sa.Column('uuid', sa.String(length=32), nullable=False),
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('description', sa.String(length=255), nullable=True),
sa.Column('created', sa.DateTime(), nullable=False),
sa.Column('created_by_uuid', sa.String(length=32), nullable=False),
sa.Column('cognized', sa.DateTime(), nullable=True),
sa.Column('cognized_by_uuid', sa.String(length=32), nullable=True),
sa.Column('rowcount', sa.Integer(), nullable=True),
sa.Column('complete', sa.Boolean(), nullable=False),
sa.Column('executed', sa.DateTime(), nullable=True),
sa.Column('executed_by_uuid', sa.String(length=32), nullable=True),
sa.Column('purge', sa.Date(), nullable=True),
sa.Column('notes', sa.Text(), nullable=True),
sa.Column('params', rattail.db.types.JSONTextDict(), nullable=True),
sa.Column('extra_data', sa.Text(), nullable=True),
sa.Column('status_code', sa.Integer(), nullable=True),
sa.Column('status_text', sa.String(length=255), nullable=True),
sa.Column('order_uuid', sa.String(length=32), nullable=True),
sa.Column('mode', sa.Integer(), nullable=False),
sa.Column('email_address', sa.String(length=255), nullable=True),
sa.Column('phone_number', sa.String(length=20), nullable=True),
sa.ForeignKeyConstraint(['cognized_by_uuid'], ['user.uuid'], name='custorder_batch_fk_cognized_by'),
sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'], name='custorder_batch_fk_created_by'),
sa.ForeignKeyConstraint(['customer_uuid'], ['customer.uuid'], name='custorder_batch_fk_customer'),
sa.ForeignKeyConstraint(['executed_by_uuid'], ['user.uuid'], name='custorder_batch_fk_executed_by'),
sa.ForeignKeyConstraint(['order_uuid'], ['custorder.uuid'], name='custorder_batch_fk_order'),
sa.ForeignKeyConstraint(['person_uuid'], ['person.uuid'], name='custorder_batch_fk_person'),
sa.ForeignKeyConstraint(['store_uuid'], ['store.uuid'], name='custorder_batch_fk_store'),
sa.PrimaryKeyConstraint('uuid')
)
# custorder_batch_row
op.create_table('custorder_batch_row',
sa.Column('product_uuid', sa.String(length=32), nullable=True),
sa.Column('product_brand', sa.String(length=100), nullable=True),
sa.Column('product_description', sa.String(length=60), nullable=True),
sa.Column('product_size', sa.String(length=30), nullable=True),
sa.Column('product_unit_of_measure', sa.String(length=4), nullable=False),
sa.Column('department_number', sa.Integer(), nullable=True),
sa.Column('department_name', sa.String(length=30), nullable=True),
sa.Column('case_quantity', sa.Numeric(precision=10, scale=4), nullable=True),
sa.Column('cases_ordered', sa.Numeric(precision=10, scale=4), nullable=True),
sa.Column('units_ordered', sa.Numeric(precision=10, scale=4), nullable=True),
sa.Column('product_unit_cost', sa.Numeric(precision=9, scale=5), nullable=True),
sa.Column('unit_price', sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('discount_percent', sa.Numeric(precision=5, scale=3), nullable=False),
sa.Column('total_price', sa.Numeric(precision=8, scale=3), nullable=True),
sa.Column('paid_amount', sa.Numeric(precision=8, scale=3), nullable=False),
sa.Column('payment_transaction_number', sa.String(length=8), nullable=True),
sa.Column('uuid', sa.String(length=32), nullable=False),
sa.Column('batch_uuid', sa.String(length=32), nullable=False),
sa.Column('sequence', sa.Integer(), nullable=False),
sa.Column('status_code', sa.Integer(), nullable=True),
sa.Column('status_text', sa.String(length=255), nullable=True),
sa.Column('modified', sa.DateTime(), nullable=True),
sa.Column('removed', sa.Boolean(), nullable=False),
sa.Column('item_entry', sa.String(length=32), nullable=True),
sa.Column('item_uuid', sa.String(length=32), nullable=True),
sa.ForeignKeyConstraint(['batch_uuid'], ['custorder_batch.uuid'], name='custorder_batch_row_fk_batch_uuid'),
sa.ForeignKeyConstraint(['item_uuid'], ['custorder_item.uuid'], name='custorder_batch_row_fk_item'),
sa.ForeignKeyConstraint(['product_uuid'], ['product.uuid'], name='custorder_batch_row_fk_product'),
sa.PrimaryKeyConstraint('uuid')
)
def downgrade():
# custorder_batch*
op.drop_table('custorder_batch_row')
op.drop_table('custorder_batch')
# custorder
op.drop_constraint('custorder_fk_store', 'custorder', type_='foreignkey')
op.drop_constraint('custorder_fk_created_by', 'custorder', type_='foreignkey')
op.drop_column('custorder', 'store_uuid')
op.drop_column('custorder', 'created_by_uuid')
# op.drop_column('custorder', 'phone_number')
# op.drop_column('custorder', 'email_address')

View file

@ -71,5 +71,6 @@ from .batch.newproduct import NewProductBatch, NewProductBatchRow
from .batch.pricing import PricingBatch, PricingBatchRow from .batch.pricing import PricingBatch, PricingBatchRow
from .batch.product import ProductBatch, ProductBatchRow from .batch.product import ProductBatch, ProductBatchRow
from .batch.purchase import PurchaseBatch, PurchaseBatchRow, PurchaseBatchRowClaim, PurchaseBatchCredit from .batch.purchase import PurchaseBatch, PurchaseBatchRow, PurchaseBatchRowClaim, PurchaseBatchCredit
from .batch.custorder import CustomerOrderBatch, CustomerOrderBatchRow
from .batch.vendorcatalog import VendorCatalog, VendorCatalogRow from .batch.vendorcatalog import VendorCatalog, VendorCatalogRow
from .batch.vendorinvoice import VendorInvoice, VendorInvoiceRow from .batch.vendorinvoice import VendorInvoice, VendorInvoiceRow

View file

@ -0,0 +1,115 @@
# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2020 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/>.
#
################################################################################
"""
Models for customer order batches
"""
from __future__ import unicode_literals, absolute_import
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.declarative import declared_attr
from rattail.db.model import (Base, BatchMixin, BatchRowMixin,
CustomerOrderBase, CustomerOrderItemBase,
CustomerOrder, CustomerOrderItem)
class CustomerOrderBatch(BatchMixin, CustomerOrderBase, Base):
"""
Hopefully generic batch used for entering new customer orders into the
system, as well as fulfilling them along the way, etc.
"""
batch_key = 'custorder'
__tablename__ = 'custorder_batch'
__batchrow_class__ = 'CustomerOrderBatchRow'
model_title_plural = "Customer Order Batches"
@declared_attr
def __table_args__(cls):
return cls.__batch_table_args__() + cls.__customer_order_table_args__() + (
sa.ForeignKeyConstraint(['order_uuid'], ['custorder.uuid'],
name='custorder_batch_fk_order'),
)
STATUS_OK = 1
STATUS = {
STATUS_OK : "ok",
}
order_uuid = sa.Column(sa.String(length=32), nullable=True)
order = orm.relationship(
CustomerOrder,
doc="""
Reference to the customer order with which the batch is associated.
May be null, e.g. in the case of a "new order" batch.
""",
backref=orm.backref(
'batches',
order_by='CustomerOrderBatch.id',
doc="""
List of batches associated with the customer order.
"""))
mode = sa.Column(sa.Integer(), nullable=False, doc="""
Numeric "mode" for the customer order batch, to indicate new/fulfilling etc.
""")
class CustomerOrderBatchRow(BatchRowMixin, CustomerOrderItemBase, Base):
"""
Row of data within a customer order batch.
"""
__tablename__ = 'custorder_batch_row'
__batch_class__ = CustomerOrderBatch
@declared_attr
def __table_args__(cls):
return cls.__batchrow_table_args__() + cls.__customer_order_item_table_args__() + (
sa.ForeignKeyConstraint(['item_uuid'], ['custorder_item.uuid'],
name='custorder_batch_row_fk_item'),
)
STATUS_OK = 1
STATUS_PRODUCT_NOT_FOUND = 2
STATUS = {
STATUS_OK : "ok",
STATUS_PRODUCT_NOT_FOUND : "product not found",
}
item_entry = sa.Column(sa.String(length=32), nullable=True, doc="""
Raw entry value, as obtained from the initial data source, and which is
used to locate the product within the system. This raw value is preserved
in case the initial lookup fails and a refresh must attempt further
lookup(s) later. Only used by certain batch handlers in practice.
""")
item_uuid = sa.Column(sa.String(length=32), nullable=True)
item = orm.relationship(
CustomerOrderItem,
doc="""
Reference to the customer order line item with which the batch row is
associated. May be null, e.g. in the case of a "new order" batch.
""")

View file

@ -35,7 +35,7 @@ from sqlalchemy.ext.orderinglist import ordering_list
from sqlalchemy.ext.declarative import declared_attr from sqlalchemy.ext.declarative import declared_attr
from rattail.db.model import Base, uuid_column from rattail.db.model import Base, uuid_column
from rattail.db.model import Customer, Person, Product, User from rattail.db.model import Store, Customer, Person, Product, User
@six.python_2_unicode_compatible @six.python_2_unicode_compatible
@ -52,12 +52,24 @@ class CustomerOrderBase(object):
def __customer_order_table_args__(cls): def __customer_order_table_args__(cls):
table_name = cls.__tablename__ table_name = cls.__tablename__
return ( return (
sa.ForeignKeyConstraint(['store_uuid'], ['store.uuid'],
name='{}_fk_store'.format(table_name)),
sa.ForeignKeyConstraint(['customer_uuid'], ['customer.uuid'], sa.ForeignKeyConstraint(['customer_uuid'], ['customer.uuid'],
name='{}_fk_customer'.format(table_name)), name='{}_fk_customer'.format(table_name)),
sa.ForeignKeyConstraint(['person_uuid'], ['person.uuid'], sa.ForeignKeyConstraint(['person_uuid'], ['person.uuid'],
name='{}_fk_person'.format(table_name)), name='{}_fk_person'.format(table_name)),
) )
store_uuid = sa.Column(sa.String(length=32), nullable=True)
@declared_attr
def store(cls):
return orm.relationship(
Store,
doc="""
Reference to the store to which the order applies.
""")
customer_uuid = sa.Column(sa.String(length=32), nullable=True) customer_uuid = sa.Column(sa.String(length=32), nullable=True)
@declared_attr @declared_attr
@ -78,8 +90,12 @@ class CustomerOrderBase(object):
Reference to the person to whom the order applies. Reference to the person to whom the order applies.
""") """)
created = sa.Column(sa.DateTime(), nullable=False, default=datetime.datetime.utcnow, doc=""" phone_number = sa.Column(sa.String(length=20), nullable=True, doc="""
Date and time when the order/batch was first created. Customer contact phone number for sake of this order.
""")
email_address = sa.Column(sa.String(length=255), nullable=True, doc="""
Customer contact email address for sake of this order.
""") """)
@ -90,12 +106,30 @@ class CustomerOrder(CustomerOrderBase, Base):
""" """
__tablename__ = 'custorder' __tablename__ = 'custorder'
@declared_attr
def __table_args__(cls):
return cls.__customer_order_table_args__() + (
sa.ForeignKeyConstraint(['created_by_uuid'], ['user.uuid'],
name='custorder_fk_created_by'),
)
uuid = uuid_column() uuid = uuid_column()
id = sa.Column(sa.Integer(), doc=""" id = sa.Column(sa.Integer(), doc="""
Numeric, auto-increment ID for the order. Numeric, auto-increment ID for the order.
""") """)
created = sa.Column(sa.DateTime(), nullable=False, default=datetime.datetime.utcnow, doc="""
Date and time when the order/batch was first created.
""")
created_by_uuid = sa.Column(sa.String(length=32), nullable=True)
created_by = orm.relationship(
User,
doc="""
Reference to the user who initially created the order/batch.
""")
status_code = sa.Column(sa.Integer(), nullable=False) status_code = sa.Column(sa.Integer(), nullable=False)
items = orm.relationship('CustomerOrderItem', back_populates='order', items = orm.relationship('CustomerOrderItem', back_populates='order',
@ -127,12 +161,6 @@ class CustomerOrderItemBase(object):
name='{}_fk_product'.format(table_name)), name='{}_fk_product'.format(table_name)),
) )
sequence = sa.Column(sa.Integer(), nullable=False, doc="""
Numeric sequence for the item, i.e. its "line number". These values should
obviously increment in sequence and be unique within the context of a
single order.
""")
product_uuid = sa.Column(sa.String(length=32), nullable=True) product_uuid = sa.Column(sa.String(length=32), nullable=True)
@declared_attr @declared_attr
@ -238,6 +266,12 @@ class CustomerOrderItem(CustomerOrderItemBase, Base):
Reference to the :class:`CustomerOrder` instance to which the item belongs. Reference to the :class:`CustomerOrder` instance to which the item belongs.
""") """)
sequence = sa.Column(sa.Integer(), nullable=False, doc="""
Numeric sequence for the item, i.e. its "line number". These values should
obviously increment in sequence and be unique within the context of a
single order.
""")
status_code = sa.Column(sa.Integer(), nullable=False) status_code = sa.Column(sa.Integer(), nullable=False)

View file

@ -72,6 +72,15 @@ BATCH_ACTION = {
} }
CUSTORDER_BATCH_MODE_CREATING = 10
CUSTORDER_BATCH_MODE_GATHERING = 20
CUSTORDER_BATCH_MODE = {
CUSTORDER_BATCH_MODE_CREATING : "creating",
CUSTORDER_BATCH_MODE_GATHERING : "gathering",
}
EMAIL_ATTEMPT_CREATED = 0 EMAIL_ATTEMPT_CREATED = 0
EMAIL_ATTEMPT_SUCCESS = 1 EMAIL_ATTEMPT_SUCCESS = 1
EMAIL_ATTEMPT_FAILURE = 2 EMAIL_ATTEMPT_FAILURE = 2