rattail/rattail/db/model/customers.py
Lance Edgar f27cc37c4e Add Customer.number field to schema
for those who want a proper integer key on the customer table
2017-05-11 13:51:57 -05:00

253 lines
8.2 KiB
Python

# -*- coding: utf-8; -*-
################################################################################
#
# Rattail -- Retail Software Framework
# Copyright © 2010-2017 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 Affero 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 Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with Rattail. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
Data Models for Customers
"""
from __future__ import unicode_literals, absolute_import
import six
import sqlalchemy as sa
from sqlalchemy import orm
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.ext.orderinglist import ordering_list
from rattail.db.model import Base, uuid_column, getset_factory
from rattail.db.model import PhoneNumber, EmailAddress, MailingAddress, Person
@six.python_2_unicode_compatible
class Customer(Base):
"""
Represents a customer account.
Customer accounts may consist of more than one person, in some cases.
"""
__tablename__ = 'customer'
__versioned__ = {}
uuid = uuid_column()
id = sa.Column(sa.String(length=20), nullable=True, doc="""
String ID for the customer, if known/relevant. This may or may not
correspond to the :attr:`number`, depending on your system.
""")
number = sa.Column(sa.Integer(), nullable=True, doc="""
Customer number, if known/relevant. This may or may not correspond to the
:attr:`id`, depending on your system.
""")
name = sa.Column(sa.String(length=255))
email_preference = sa.Column(sa.Integer())
def __str__(self):
return self.name or ''
def add_email_address(self, address, type='Home'):
email = CustomerEmailAddress(address=address, type=type)
self.emails.append(email)
def add_phone_number(self, number, type='Home'):
phone = CustomerPhoneNumber(number=number, type=type)
self.phones.append(phone)
class CustomerPhoneNumber(PhoneNumber):
"""
Represents a phone (or fax) number associated with a :class:`Customer`.
"""
__mapper_args__ = {'polymorphic_identity': 'Customer'}
Customer.phones = orm.relationship(
CustomerPhoneNumber,
backref='customer',
primaryjoin=CustomerPhoneNumber.parent_uuid == Customer.uuid,
foreign_keys=[CustomerPhoneNumber.parent_uuid],
collection_class=ordering_list('preference', count_from=1),
order_by=CustomerPhoneNumber.preference,
cascade='save-update, merge, delete, delete-orphan')
Customer.phone = orm.relationship(
CustomerPhoneNumber,
primaryjoin=sa.and_(
CustomerPhoneNumber.parent_uuid == Customer.uuid,
CustomerPhoneNumber.preference == 1),
foreign_keys=[CustomerPhoneNumber.parent_uuid],
uselist=False,
viewonly=True)
class CustomerEmailAddress(EmailAddress):
"""
Represents an email address associated with a :class:`Customer`.
"""
__mapper_args__ = {'polymorphic_identity': 'Customer'}
Customer.emails = orm.relationship(
CustomerEmailAddress,
backref='customer',
primaryjoin=CustomerEmailAddress.parent_uuid == Customer.uuid,
foreign_keys=[CustomerEmailAddress.parent_uuid],
collection_class=ordering_list('preference', count_from=1),
order_by=CustomerEmailAddress.preference,
cascade='save-update, merge, delete, delete-orphan')
Customer.email = orm.relationship(
CustomerEmailAddress,
primaryjoin=sa.and_(
CustomerEmailAddress.parent_uuid == Customer.uuid,
CustomerEmailAddress.preference == 1),
foreign_keys=[CustomerEmailAddress.parent_uuid],
uselist=False,
viewonly=True)
class CustomerMailingAddress(MailingAddress):
"""
Represents a mailing address for a customer
"""
__mapper_args__ = {'polymorphic_identity': 'Customer'}
Customer.addresses = orm.relationship(
CustomerMailingAddress,
backref='customer',
primaryjoin=CustomerMailingAddress.parent_uuid == Customer.uuid,
foreign_keys=[CustomerMailingAddress.parent_uuid],
collection_class=ordering_list('preference', count_from=1),
order_by=CustomerMailingAddress.preference,
cascade='all, delete-orphan')
Customer.address = orm.relationship(
CustomerMailingAddress,
primaryjoin=sa.and_(
CustomerMailingAddress.parent_uuid == Customer.uuid,
CustomerMailingAddress.preference == 1),
foreign_keys=[CustomerMailingAddress.parent_uuid],
uselist=False,
viewonly=True)
@six.python_2_unicode_compatible
class CustomerGroup(Base):
"""
Represents an arbitrary group to which customers may belong.
"""
__tablename__ = 'customer_group'
__versioned__ = {}
uuid = uuid_column()
id = sa.Column(sa.String(length=20))
name = sa.Column(sa.String(length=255))
def __str__(self):
return self.name or ''
class CustomerGroupAssignment(Base):
"""
Represents the assignment of a customer to a group.
"""
__tablename__ = 'customer_x_group'
__table_args__ = (
sa.ForeignKeyConstraint(['group_uuid'], ['customer_group.uuid'], name='customer_x_group_fk_group'),
sa.ForeignKeyConstraint(['customer_uuid'], ['customer.uuid'], name='customer_x_group_fk_customer'),
)
uuid = uuid_column()
customer_uuid = sa.Column(sa.String(length=32), nullable=False)
group_uuid = sa.Column(sa.String(length=32), nullable=False)
ordinal = sa.Column(sa.Integer(), nullable=False)
group = orm.relationship(CustomerGroup)
Customer._groups = orm.relationship(
CustomerGroupAssignment, backref='customer',
collection_class=ordering_list('ordinal', count_from=1),
order_by=CustomerGroupAssignment.ordinal,
cascade='save-update, merge, delete, delete-orphan')
Customer.groups = association_proxy(
'_groups', 'group',
getset_factory=getset_factory,
creator=lambda g: CustomerGroupAssignment(group=g))
class CustomerPerson(Base):
"""
Represents the association between a person and a customer account.
"""
__tablename__ = 'customer_x_person'
__table_args__ = (
sa.ForeignKeyConstraint(['customer_uuid'], ['customer.uuid'], name='customer_x_person_fk_customer'),
sa.ForeignKeyConstraint(['person_uuid'], ['person.uuid'], name='customer_x_person_fk_person'),
)
uuid = uuid_column()
customer_uuid = sa.Column(sa.String(length=32), nullable=False)
person_uuid = sa.Column(sa.String(length=32), nullable=False)
ordinal = sa.Column(sa.Integer(), nullable=False)
customer = orm.relationship(Customer, back_populates='_people')
person = orm.relationship(Person, back_populates='_customers')
Customer._people = orm.relationship(
CustomerPerson, back_populates='customer',
primaryjoin=CustomerPerson.customer_uuid == Customer.uuid,
collection_class=ordering_list('ordinal', count_from=1),
order_by=CustomerPerson.ordinal,
cascade='save-update, merge, delete, delete-orphan')
Customer.people = association_proxy(
'_people', 'person',
getset_factory=getset_factory,
creator=lambda p: CustomerPerson(person=p))
Customer._person = orm.relationship(
CustomerPerson,
primaryjoin=sa.and_(
CustomerPerson.customer_uuid == Customer.uuid,
CustomerPerson.ordinal == 1),
uselist=False,
viewonly=True)
Customer.person = association_proxy(
'_person', 'person',
getset_factory=getset_factory)
Person._customers = orm.relationship(
CustomerPerson, back_populates='person',
primaryjoin=CustomerPerson.person_uuid == Person.uuid,
cascade='all, delete-orphan',
viewonly=True)
Person.customers = association_proxy('_customers', 'customer',
getset_factory=getset_factory,
creator=lambda c: CustomerPerson(customer=c))