Remove legacy 'forms' package and templates

yay!
This commit is contained in:
Lance Edgar 2018-02-11 22:46:35 -06:00
parent 66769ab34b
commit d0b78babd2
26 changed files with 0 additions and 2051 deletions

View file

@ -1,39 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Forms
"""
from __future__ import unicode_literals, absolute_import
from formencode import Schema
from .core import Form, Field, FieldSet, GenericFieldSet
from .simpleform import SimpleForm, FormRenderer
from .alchemy import AlchemyForm
from .fields import AssociationProxyField
from .renderers import *
from . import fields
from . import renderers
from . import validators

View file

@ -1,117 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
FormAlchemy Forms
"""
from __future__ import unicode_literals, absolute_import
from rattail.core import Object
import formalchemy as fa
from pyramid.renderers import render
from webhelpers2.html import HTML, tags
from tailbone.db import Session
class TemplateEngine(fa.templates.TemplateEngine):
"""
Mako template engine for FormAlchemy.
"""
def render(self, template, prefix='/forms/', suffix='.mako', **kwargs):
template = ''.join((prefix, template, suffix))
return render(template, kwargs)
class AlchemyForm(Object):
"""
Form to contain a :class:`formalchemy.FieldSet` instance.
"""
id = None
create_label = "Create"
update_label = "Save"
allow_successive_creates = False
def __init__(self, request, fieldset, session=None, csrf_field='_csrf', **kwargs):
super(AlchemyForm, self).__init__(**kwargs)
self.request = request
self.fieldset = fieldset
self.session = session
self.csrf_field = csrf_field
def _get_readonly(self):
return self.fieldset.readonly
def _set_readonly(self, val):
self.fieldset.readonly = val
readonly = property(_get_readonly, _set_readonly)
@property
def successive_create_label(self):
return "%s and continue" % self.create_label
def csrf(self, name=None):
"""
NOTE: this method was copied from `pyramid_simpleform.FormRenderer`
Returns the CSRF hidden input. Creates new CSRF token
if none has been assigned yet.
The name of the hidden field is **_csrf** by default.
"""
name = name or self.csrf_field
token = self.request.session.get_csrf_token()
if token is None:
token = self.request.session.new_csrf_token()
return tags.hidden(name, value=token)
def csrf_token(self, name=None):
"""
NOTE: this method was copied from `pyramid_simpleform.FormRenderer`
Convenience function. Returns CSRF hidden tag inside hidden DIV.
"""
return HTML.tag("div", self.csrf(name), style="display:none;")
def render(self, **kwargs):
kwargs['form'] = self
if self.readonly:
template = '/forms/form_readonly.mako'
else:
template = '/forms/form.mako'
return render(template, kwargs)
def render_fields(self):
return self.fieldset.render()
def save(self):
self.fieldset.sync()
self.session.flush()
def validate(self):
self.fieldset.rebind(data=self.request.params)
return self.fieldset.validate()

View file

@ -1,119 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Forms Core
"""
from __future__ import unicode_literals, absolute_import
from rattail.util import OrderedDict, prettify
import formalchemy
from formalchemy.helpers import content_tag
from pyramid.renderers import render
class Form(object):
"""
Base class for all forms.
"""
create_label = "Create"
update_label = "Save"
def __init__(self, request, readonly=False, action_url=None):
self.request = request
self.readonly = readonly
self.action_url = action_url
def render(self, **kwargs):
kwargs.setdefault('form', self)
if self.readonly:
template = '/forms/form_readonly.mako'
else:
template = '/forms/form.mako'
return render(template, kwargs)
def render_fields(self, **kwargs):
kwargs.setdefault('fieldset', self.fieldset)
if self.readonly:
template = '/forms/fieldset_readonly.mako'
else:
template = '/forms/fieldset.mako'
return render(template, kwargs)
class Field(object):
"""
Manually create instances of this class to populate a simple form.
"""
def __init__(self, name, value=None, label=None, requires_label=True):
self.name = name
self.value = value
self._label = label or prettify(self.name)
self.requires_label = requires_label
def is_required(self):
return True
def label(self):
return self._label
def label_tag(self, **html_options):
"""
Logic stolen from FormAlchemy so all fields can render their own label.
Original docstring follows.
return the <label /> tag for the field.
"""
html_options.update(for_=self.name)
if 'class_' in html_options:
html_options['class_'] += self.is_required() and ' field_req' or ' field_opt'
else:
html_options['class_'] = self.is_required() and 'field_req' or 'field_opt'
return content_tag('label', self.label(), **html_options)
def render_readonly(self):
if self.value is None:
return ''
return unicode(self.value)
class FieldSet(object):
"""
Generic fieldset for use with manually-created simple forms.
"""
def __init__(self):
self.fields = OrderedDict()
self.render_fields = self.fields
class GenericFieldSet(formalchemy.FieldSet):
"""
FieldSet class based on FormAlchemy, but without the SQLAlchemy magic.
"""
__sa__ = False
_bound_pk = None
data = None
prettify = staticmethod(prettify)

View file

@ -1,106 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
FormAlchemy Fields
"""
from __future__ import unicode_literals, absolute_import
import formalchemy as fa
def AssociationProxyField(name, **kwargs):
"""
Returns a FormAlchemy ``Field`` class which is aware of association
proxies.
"""
class ProxyField(fa.Field):
def sync(self):
if not self.is_readonly():
setattr(self.parent.model, self.name,
self.renderer.deserialize())
def value(obj):
return getattr(obj, name, None)
kwargs.setdefault('value', value)
return ProxyField(name, **kwargs)
class DefaultEmailField(fa.Field):
"""
Generic field for view/edit of default email address for a contact
"""
def __init__(self, name=None, **kwargs):
kwargs.setdefault('value', self.value)
if 'renderer' not in kwargs:
from tailbone.forms.renderers import EmailFieldRenderer
kwargs['renderer'] = EmailFieldRenderer
super(DefaultEmailField, self).__init__(name, **kwargs)
def value(self, contact):
if contact.emails:
return contact.emails[0].address
def sync(self):
if not self.is_readonly():
address = self._deserialize()
contact = self.parent.model
if contact.emails:
if address:
email = contact.emails[0]
email.address = address
else:
contact.emails.pop(0)
elif address:
email = contact.add_email_address(address)
class DefaultPhoneField(fa.Field):
"""
Generic field for view/edit of default phone number for a contact
"""
def __init__(self, name=None, **kwargs):
kwargs.setdefault('value', self.value)
super(DefaultPhoneField, self).__init__(name, **kwargs)
def value(self, contact):
if contact.phones:
return contact.phones[0].number
def sync(self):
if not self.is_readonly():
number = self._deserialize()
contact = self.parent.model
if contact.phones:
if number:
phone = contact.phones[0]
phone.number = number
else:
contact.phones.pop(0)
elif number:
phone = contact.add_phone_number(number)

View file

@ -1,51 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
FormAlchemy Field Renderers
"""
from __future__ import unicode_literals, absolute_import
from .core import CustomFieldRenderer, DateFieldRenderer
from .common import (StrippedTextFieldRenderer, CodeTextAreaFieldRenderer, AutocompleteFieldRenderer,
DecimalFieldRenderer, CurrencyFieldRenderer, QuantityFieldRenderer,
DateTimeFieldRenderer, DateTimePrettyFieldRenderer, LocalDateTimeFieldRenderer, TimeFieldRenderer,
EmailFieldRenderer, EnumFieldRenderer, YesNoFieldRenderer)
from .files import FileFieldRenderer
from .people import PersonFieldRenderer, PeopleFieldRenderer, CustomerFieldRenderer
from .users import UserFieldRenderer, PermissionsFieldRenderer
from .employees import EmployeeFieldRenderer
from .stores import StoreFieldRenderer
from .vendors import VendorFieldRenderer, PurchaseFieldRenderer
from .products import (GPCFieldRenderer, ScancodeFieldRenderer,
DepartmentFieldRenderer, SubdepartmentFieldRenderer, CategoryFieldRenderer,
BrandFieldRenderer, ProductFieldRenderer,
CostFieldRenderer, PriceFieldRenderer, PriceWithExpirationFieldRenderer)
from .custorders import CustomerOrderFieldRenderer
from .batch import BatchIDFieldRenderer, HandheldBatchFieldRenderer, HandheldBatchesFieldRenderer

View file

@ -1,103 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Batch Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import os
import stat
import random
import formalchemy as fa
from webhelpers2.html import tags, HTML
from tailbone.forms.renderers import FileFieldRenderer as BaseFileFieldRenderer
class BatchIDFieldRenderer(fa.FieldRenderer):
"""
Renderer for batch ID fields.
"""
def render_readonly(self, **kwargs):
try:
batch_id = self.raw_value
except AttributeError:
# this can happen when creating a new batch, b/c the default value
# comes from a sequence
pass
else:
if batch_id:
return '{:08d}'.format(batch_id)
return ''
class FileFieldRenderer(BaseFileFieldRenderer):
"""
Custom file field renderer for batches based on a single source data file.
In edit mode, shows a file upload field. In readonly mode, shows the
filename and its size.
"""
def get_size(self):
size = super(FileFieldRenderer, self).get_size()
if size:
return size
batch = self.field.parent.model
path = batch.filepath(self.request.rattail_config, filename=self.field.value)
if os.path.isfile(path):
return os.stat(path)[stat.ST_SIZE]
return 0
def render(self, **kwargs):
return BaseFileFieldRenderer.render(self, **kwargs)
class HandheldBatchFieldRenderer(fa.FieldRenderer):
"""
Renderer for inventory batch's "handheld batch" field.
"""
def render_readonly(self, **kwargs):
batch = self.raw_value
if batch:
return tags.link_to(
batch.id_str,
self.request.route_url('batch.handheld.view', uuid=batch.uuid))
return ''
class HandheldBatchesFieldRenderer(fa.FieldRenderer):
"""
Renders a list of associated handheld batches, for a given (presumably
inventory or labels) batch.
"""
def render_readonly(self, **kwargs):
items = ''
for handheld in self.raw_value:
text = tags.link_to(handheld.handheld.id_str,
self.request.route_url('batch.handheld.view', uuid=handheld.handheld_uuid))
items += HTML.tag('li', c=text)
return HTML.tag('ul', c=items)

View file

@ -1,63 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Batch Field Renderers
"""
from __future__ import unicode_literals
import os
import stat
import random
from formalchemy.ext import fsblob
class BounceMessageFieldRenderer(fsblob.FileFieldRenderer):
"""
Custom file field renderer for email bounce messages. In readonly mode,
shows the filename and size.
"""
@classmethod
def new(cls, request, handler):
name = 'Configured%s_%s' % (cls.__name__, unicode(random.random())[2:])
return type(str(name), (cls,), dict(request=request, handler=handler))
@property
def storage_path(self):
return self.handler.root_msgdir
def get_size(self):
size = super(BounceMessageFieldRenderer, self).get_size()
if size:
return size
bounce = self.field.parent.model
path = os.path.join(self.handler.msgpath(bounce))
if os.path.isfile(path):
return os.stat(path)[stat.ST_SIZE]
return 0
def get_url(self, filename):
bounce = self.field.parent.model
return self.request.route_url('emailbounces.download', uuid=bounce.uuid)

View file

@ -1,315 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Common Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import datetime
from rattail.time import localtime, make_utc
from rattail.util import pretty_quantity
import formalchemy as fa
from formalchemy import fields as fa_fields, helpers as fa_helpers
from pyramid.renderers import render
from webhelpers2.html import HTML, tags
from tailbone.util import pretty_datetime, raw_datetime
class StrippedTextFieldRenderer(fa.TextFieldRenderer):
"""
Standard text field renderer, which strips whitespace from either end of
the input value on deserialization.
"""
def deserialize(self):
value = super(StrippedTextFieldRenderer, self).deserialize()
if value is not None:
return value.strip()
class CodeTextAreaFieldRenderer(fa.TextAreaFieldRenderer):
def render_readonly(self, **kwargs):
value = self.raw_value
if not value:
return ''
return HTML.tag('pre', c=value)
def render(self, **kwargs):
kwargs.setdefault('size', (80, 8))
return super(CodeTextAreaFieldRenderer, self).render(**kwargs)
class AutocompleteFieldRenderer(fa.FieldRenderer):
"""
Custom renderer for an autocomplete field.
"""
service_route = None
width = '300px'
@property
def focus_name(self):
return self.name + '-textbox'
@property
def needs_focus(self):
return not bool(self.value or self.field_value)
@property
def field_display(self):
return self.raw_value
@property
def field_value(self):
return self.value
@property
def service_url(self):
return self.request.route_url(self.service_route)
def render(self, options=None, **kwargs):
if kwargs.pop('autocomplete', True):
return self.render_autocomplete(**kwargs)
# 'selected' is a kwarg for autocomplete template *and* select tag
kwargs.pop('selected', None)
return self.render_dropdown(options, **kwargs)
def render_autocomplete(self, **kwargs):
kwargs.setdefault('field_name', self.name)
kwargs.setdefault('field_value', self.field_value)
kwargs.setdefault('field_display', self.field_display)
kwargs.setdefault('service_url', self.service_url)
kwargs.setdefault('width', self.width)
return render('/forms/field_autocomplete.mako', kwargs)
def render_dropdown(self, options, **kwargs):
# NOTE: this logic copied from formalchemy.fields.SelectFieldRenderer.render()
kwargs.setdefault('auto-enhance', 'true')
if callable(options):
L = fa_fields._normalized_options(options(self.field.parent))
if not self.field.is_required() and not self.field.is_collection:
L.insert(0, self.field._null_option)
else:
L = list(options)
if len(L) > 0:
if len(L[0]) == 2:
L = [(k, self.stringify_value(v)) for k, v in L]
else:
L = [fa_fields._stringify(k) for k in L]
return fa_fields.h.select(self.name, self.value, L, **kwargs)
def render_readonly(self, **kwargs):
value = self.field_display
if value is None:
return u''
return unicode(value)
class DateTimeFieldRenderer(fa.DateTimeFieldRenderer):
"""
This renderer assumes the datetime field value is in UTC, and will convert
it to the local time zone before rendering it in the standard "raw" format.
"""
def render_readonly(self, **kwargs):
value = self.raw_value
if not value:
return ''
return raw_datetime(self.request.rattail_config, value)
class DateTimePrettyFieldRenderer(fa.DateTimeFieldRenderer):
"""
Custom date/time field renderer, which displays a "pretty" value in
read-only mode, leveraging config to show the correct timezone.
"""
def render_readonly(self, **kwargs):
value = self.raw_value
if not value:
return ''
return pretty_datetime(self.request.rattail_config, value)
class LocalDateTimeFieldRenderer(fa.DateTimeFieldRenderer):
"""
This renderer assumes the datetime field value is "naive" in local time zone.
"""
def render_readonly(self, **kwargs):
value = self.raw_value
if not value:
return ''
value = localtime(self.request.rattail_config, value)
return raw_datetime(self.request.rattail_config, value)
class TimeFieldRenderer(fa.TimeFieldRenderer):
"""
Custom renderer for time fields. In edit mode, renders a simple text
input, which is expected to become a 'timepicker' widget in the UI.
However the particular magic required for that lives in 'tailbone.js'.
"""
format = '%I:%M %p'
def render(self, **kwargs):
kwargs.setdefault('class_', 'timepicker')
return fa_helpers.text_field(self.name, value=self.value, **kwargs)
def render_readonly(self, **kwargs):
return self.render_value(self.raw_value)
def render_value(self, value):
value = self.convert_value(value)
if isinstance(value, datetime.time):
return value.strftime(self.format)
return ''
def convert_value(self, value):
if isinstance(value, datetime.datetime):
if not value.tzinfo:
value = make_utc(value, tzinfo=True)
return localtime(self.request.rattail_config, value).time()
return value
def stringify_value(self, value, as_html=False):
if not as_html:
return self.render_value(value)
return super(TimeFieldRenderer, self).stringify_value(value, as_html=as_html)
def _serialized_value(self):
return self.params.getone(self.name)
def deserialize(self):
value = self._serialized_value()
if value:
try:
return datetime.datetime.strptime(value, self.format).time()
except ValueError:
pass
class EmailFieldRenderer(fa.TextFieldRenderer):
"""
Renderer for email address fields
"""
def render_readonly(self, **kwargs):
address = self.raw_value
if not address:
return ''
return tags.link_to(address, 'mailto:{}'.format(address))
class EnumFieldRenderer(fa_fields.SelectFieldRenderer):
"""
Renderer for simple enumeration fields.
"""
enumeration = {}
render_key = False
def __init__(self, arg, render_key=False):
if isinstance(arg, dict):
self.enumeration = arg
self.render_key = render_key
else:
self(arg)
def __call__(self, field):
super(EnumFieldRenderer, self).__init__(field)
return self
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
rendered = self.enumeration.get(value, unicode(value))
if self.render_key:
rendered = '{} - {}'.format(value, rendered)
return rendered
def render(self, **kwargs):
opts = [(self.enumeration[x], x) for x in self.enumeration]
if not self.field.is_required():
opts.insert(0, self.field._null_option)
return fa_fields.SelectFieldRenderer.render(self, opts, **kwargs)
class DecimalFieldRenderer(fa.FieldRenderer):
"""
Sort of generic field renderer for decimal values. You must provide the
number of places after the decimal (scale). Note that this in turn relies
on simple string formatting; the renderer does not attempt any mathematics
of its own.
"""
def __init__(self, scale):
self.scale = scale
def __call__(self, field):
super(DecimalFieldRenderer, self).__init__(field)
return self
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
fmt = '{{0:0.{0}f}}'.format(self.scale)
return fmt.format(value)
class CurrencyFieldRenderer(fa_fields.FloatFieldRenderer):
"""
Sort of generic field renderer for currency values.
"""
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
if value < 0:
return "(${:0,.2f})".format(0 - value)
return "${:0,.2f}".format(value)
class QuantityFieldRenderer(fa_fields.FloatFieldRenderer):
"""
Sort of generic field renderer for quantity values.
"""
def render_readonly(self, **kwargs):
return pretty_quantity(self.raw_value)
class YesNoFieldRenderer(fa.CheckBoxFieldRenderer):
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return u''
return u'Yes' if value else u'No'

View file

@ -1,100 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Core Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import datetime
import formalchemy as fa
from formalchemy.fields import AbstractField
from pyramid.renderers import render
class CustomFieldRenderer(fa.FieldRenderer):
"""
Base class for renderers which accept customization args, and "fake out"
FormAlchemy by pretending to still be a renderer factory when in fact it's
already dealing with a renderer instance.
"""
def __init__(self, *args, **kwargs):
if len(args) == 1 and isinstance(args[0], AbstractField):
super(CustomFieldRenderer, self).__init__(args[0])
self.init(**kwargs)
else:
assert len(args) == 0
self.init(**kwargs)
def __call__(self, field):
super(CustomFieldRenderer, self).__init__(field)
return self
def init(self, **kwargs):
pass
@property
def rattail_config(self):
return self.request.rattail_config
class DateFieldRenderer(CustomFieldRenderer, fa.DateFieldRenderer):
"""
Date field renderer which uses jQuery UI datepicker widget when rendering
in edit mode.
"""
date_format = '%Y-%m-%d'
change_year = False
def init(self, date_format=None, change_year=False):
if date_format:
self.date_format = date_format
self.change_year = change_year
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
return value.strftime(self.date_format)
def render(self, **kwargs):
kwargs['name'] = self.name
kwargs['value'] = self.value
kwargs['change_year'] = self.change_year
return render('/forms/fields/date.mako', kwargs)
def deserialize(self):
value = self._serialized_value()
if not value:
return None
try:
return datetime.datetime.strptime(value, '%Y-%m-%d')
except ValueError:
raise fa.ValidationError("Date value must be in YYYY-MM-DD format")
except Exception as error:
raise fa.ValidationError(unicode(error))
def _serialized_value(self):
return self.params.getone(self.name)

View file

@ -1,42 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Customer order field renderers
"""
from __future__ import unicode_literals, absolute_import
import formalchemy as fa
from webhelpers2.html import tags
class CustomerOrderFieldRenderer(fa.fields.SelectFieldRenderer):
"""
Renders a link to the customer order
"""
def render_readonly(self, **kwargs):
order = self.raw_value
if not order:
return ''
return tags.link_to(order, self.request.route_url('custorders.view', uuid=order.uuid))

View file

@ -1,49 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Employee Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import six
from webhelpers2.html import tags
from tailbone.forms.renderers import AutocompleteFieldRenderer
class EmployeeFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Employee` instance fields.
"""
service_route = 'employees.autocomplete'
def render_readonly(self, **kwargs):
employee = self.raw_value
if not employee:
return ''
render_name = kwargs.get('render_name', six.text_type)
title = render_name(employee)
if kwargs.get('hyperlink') and self.request.has_perm('employees.view'):
return tags.link_to(title, self.request.route_url('employees.view', uuid=employee.uuid))
return title

View file

@ -1,102 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Batch Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import os
import stat
import random
from formalchemy.ext import fsblob
from formalchemy.fields import FileFieldRenderer as Base
class FileFieldRenderer(fsblob.FileFieldRenderer):
"""
Custom file field renderer. In readonly mode, shows a filename and its
size; in edit mode, supports a single file upload.
"""
@classmethod
def new(cls, view, **kwargs):
name = b'Configured{}_{}'.format(cls.__name__, str(random.random())[2:])
return type(name, (cls,), dict(view=view, **kwargs))
@property
def request(self):
return self.view.request
@property
def storage_path(self):
return self.view.upload_dir
def get_file_path(self):
"""
Returns the absolute path to the data file.
"""
if hasattr(self, 'file_path'):
return self.file_path
return self.field.value
def get_size(self):
"""
Returns the size of the data file, in bytes.
"""
path = self.get_file_path()
if path and os.path.isfile(path):
return os.stat(path)[stat.ST_SIZE]
return 0
def get_url(self, filename):
"""
Must return a URL suitable for downloading the file
"""
url = self.get_download_url()
if url:
if callable(url):
return url(filename)
return url
return self.view.get_action_url('download', self.field.parent.model)
def get_download_url(self):
if hasattr(self, 'download_url'):
return self.download_url
def render(self, **kwargs):
return Base.render(self, **kwargs)
def render_readonly(self, **kwargs):
"""
Render the filename and the binary size in a human readable with a link
to the file itself.
"""
value = self.get_file_path()
if value:
content = '{} ({})'.format(fsblob.normalized_basename(value),
self.readable_size())
return fsblob.h.content_tag('a', content,
href=self.get_url(value), **kwargs)
return ''

View file

@ -1,81 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
People Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import six
import formalchemy as fa
from webhelpers2.html import tags, HTML
from tailbone.forms.renderers.common import AutocompleteFieldRenderer
class PersonFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Person` instance fields.
"""
service_route = 'people.autocomplete'
def render_readonly(self, **kwargs):
person = self.raw_value
if not person:
return ''
return tags.link_to(person, self.request.route_url('people.view', uuid=person.uuid))
class PeopleFieldRenderer(fa.FieldRenderer):
"""
Renderer for "people" list relationship
"""
def render_readonly(self, **kwargs):
html = ''
people = self.raw_value
if not people:
return html
for person in people:
link = tags.link_to(person, self.request.route_url('people.view', uuid=person.uuid))
html += HTML.tag('li', c=link)
html = HTML.tag('ul', c=html)
return html
class CustomerFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Customer` instance fields.
"""
service_route = 'customers.autocomplete'
def render_readonly(self, **kwargs):
customer = self.raw_value
if not customer:
return ''
text = self.render_value(customer)
return tags.link_to(text, self.request.route_url('customers.view', uuid=customer.uuid))
def render_value(self, customer):
return six.text_type(customer)

View file

@ -1,224 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Product Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import six
from rattail.gpc import GPC
from rattail.db import model
from rattail.db.util import maxlen
import formalchemy as fa
from formalchemy import TextFieldRenderer
from formalchemy.fields import SelectFieldRenderer
from webhelpers2.html import tags, literal
from tailbone.forms.renderers.common import AutocompleteFieldRenderer
from tailbone.util import pretty_datetime
class ProductFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Product` instance fields.
"""
service_route = 'products.autocomplete'
@property
def field_display(self):
product = self.raw_value
if product:
return product.full_description
return ''
def render_readonly(self, **kwargs):
product = self.raw_value
if not product:
return ""
render = kwargs.get('render_product', self.render_product)
text = render(product)
if kwargs.get('hyperlink', True):
return tags.link_to(text, self.request.route_url('products.view', uuid=product.uuid))
return text
def render_product(self, product):
return six.text_type(product)
class ProductKeyFieldRenderer(TextFieldRenderer):
"""
Base class for product key field renderers.
"""
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
value = self.render_value(value)
if kwargs.get('link'):
product = self.field.parent.model
value = tags.link_to(value, kwargs['link'](product))
return value
def render_value(self, value):
return unicode(value)
class GPCFieldRenderer(ProductKeyFieldRenderer):
"""
Renderer for :class:`rattail.gpc.GPC` fields.
"""
@property
def length(self):
# Hm, should maybe consider hard-coding this...?
return len(unicode(GPC(0)))
def render_value(self, gpc):
return gpc.pretty()
class ScancodeFieldRenderer(ProductKeyFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Product.scancode` field
"""
@property
def length(self):
return maxlen(model.Product.scancode)
class DepartmentFieldRenderer(SelectFieldRenderer):
"""
Shows the department number as well as the name.
"""
def render(self, **kwargs):
kwargs.setdefault('auto-enhance', 'true')
return super(DepartmentFieldRenderer, self).render(**kwargs)
def render_readonly(self, **kwargs):
department = self.raw_value
if not department:
return ''
if department.number:
text = '({}) {}'.format(department.number, department.name)
else:
text = department.name
return tags.link_to(text, self.request.route_url('departments.view', uuid=department.uuid))
class SubdepartmentFieldRenderer(SelectFieldRenderer):
"""
Shows a link to the subdepartment.
"""
def render_readonly(self, **kwargs):
subdept = self.raw_value
if not subdept:
return ""
if subdept.number:
text = "({}) {}".format(subdept.number, subdept.name)
else:
text = subdept.name
return tags.link_to(text, self.request.route_url('subdepartments.view', uuid=subdept.uuid))
class CategoryFieldRenderer(SelectFieldRenderer):
"""
Shows a link to the category.
"""
def render_readonly(self, **kwargs):
category = self.raw_value
if not category:
return ""
if category.code:
text = "({}) {}".format(category.code, category.name)
else:
text = category.name
return tags.link_to(text, self.request.route_url('categories.view', uuid=category.uuid))
class BrandFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Brand` instance fields.
"""
service_route = 'brands.autocomplete'
class CostFieldRenderer(fa.FieldRenderer):
"""
Renders fields which reference a ProductCost object
"""
def render_readonly(self, **kwargs):
cost = self.raw_value
if not cost:
return ''
return '${:0.2f}'.format(cost.unit_cost)
class PriceFieldRenderer(TextFieldRenderer):
"""
Renderer for fields which reference a :class:`ProductPrice` instance.
"""
def render_readonly(self, **kwargs):
price = self.field.raw_value
if price:
if not price.product.not_for_sale:
if price.price is not None and price.pack_price is not None:
if price.multiple > 1:
return literal('$ %0.2f / %u&nbsp; ($ %0.2f / %u)' % (
price.price, price.multiple,
price.pack_price, price.pack_multiple))
return literal('$ %0.2f&nbsp; ($ %0.2f / %u)' % (
price.price, price.pack_price, price.pack_multiple))
if price.price is not None:
if price.multiple > 1:
return '$ %0.2f / %u' % (price.price, price.multiple)
return '$ %0.2f' % price.price
if price.pack_price is not None:
return '$ %0.2f / %u' % (price.pack_price, price.pack_multiple)
return ''
class PriceWithExpirationFieldRenderer(PriceFieldRenderer):
"""
Price field renderer which also displays the expiration date, if present.
"""
def render_readonly(self, **kwargs):
result = super(PriceWithExpirationFieldRenderer, self).render_readonly(**kwargs)
if result:
price = self.field.raw_value
if price.ends:
result = '{0}&nbsp; ({1})'.format(
result, pretty_datetime(self.request.rattail_config, price.ends))
return result

View file

@ -1,49 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Store Field Renderers
"""
from __future__ import unicode_literals, absolute_import
from formalchemy.fields import SelectFieldRenderer
from webhelpers2.html import tags
class StoreFieldRenderer(SelectFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Store` instance fields.
"""
def render(self, **kwargs):
kwargs.setdefault('auto-enhance', 'true')
return super(StoreFieldRenderer, self).render(**kwargs)
def render_readonly(self, **kwargs):
store = self.raw_value
if not store:
return ""
text = "({}) {}".format(store.id, store.name)
if kwargs.get('hyperlink', True):
return tags.link_to(text, self.request.route_url('stores.view', uuid=store.uuid))
return text

View file

@ -1,97 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
User Field Renderers
"""
from __future__ import unicode_literals, absolute_import
import six
from rattail.db import model
from rattail.db.auth import has_permission, administrator_role
import formalchemy
from webhelpers2.html import HTML, tags
from tailbone.db import Session
class UserFieldRenderer(formalchemy.TextFieldRenderer):
"""
Renderer for :class:`rattail:rattail.db.model.User` instance fields.
"""
def render_readonly(self, **kwargs):
user = self.raw_value
if not user:
return ''
title = six.text_type(user)
if kwargs.get('hyperlink') and self.request.has_perm('users.view'):
return tags.link_to(title, self.request.route_url('users.view', uuid=user.uuid))
return title
def PermissionsFieldRenderer(permissions, include_guest=False, include_authenticated=False):
class PermissionsFieldRenderer(formalchemy.FieldRenderer):
def deserialize(self):
perms = []
i = len(self.name) + 1
for key in self.params:
if key.startswith(self.name):
perms.append(key[i:])
return perms
def _render(self, readonly=False, **kwargs):
principal = self.field.model
html = ''
for groupkey in sorted(permissions, key=lambda k: permissions[k]['label'].lower()):
inner = HTML.tag('p', c=permissions[groupkey]['label'])
perms = permissions[groupkey]['perms']
rendered = False
for key in sorted(perms, key=lambda p: perms[p]['label'].lower()):
checked = has_permission(Session(), principal, key,
include_guest=include_guest,
include_authenticated=include_authenticated)
if checked or not readonly:
label = perms[key]['label']
if readonly:
span = HTML.tag('span', c="[X]" if checked else "[ ]")
inner += HTML.tag('p', class_='perm', c=span + ' ' + label)
else:
inner += tags.checkbox(self.name + '-' + key,
checked=checked, label=label)
rendered = True
if rendered:
html += HTML.tag('div', class_='group', c=inner)
return html or "(none granted)"
def render(self, **kwargs):
return self._render(**kwargs)
def render_readonly(self, **kwargs):
return self._render(readonly=True, **kwargs)
return PermissionsFieldRenderer

View file

@ -1,60 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Vendor Field Renderers
"""
from __future__ import unicode_literals, absolute_import
from formalchemy.fields import SelectFieldRenderer
from webhelpers2.html import tags
from tailbone.forms.renderers.common import AutocompleteFieldRenderer
class VendorFieldRenderer(AutocompleteFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Vendor` instance fields.
"""
service_route = 'vendors.autocomplete'
def render_readonly(self, **kwargs):
vendor = self.raw_value
if not vendor:
return ""
text = "({}) {}".format(vendor.id, vendor.name)
if kwargs.get('hyperlink', True):
return tags.link_to(text, self.request.route_url('vendors.view', uuid=vendor.uuid))
return text
class PurchaseFieldRenderer(SelectFieldRenderer):
"""
Renderer for :class:`rattail.db.model.Purchase` relation fields.
"""
def render_readonly(self, **kwargs):
purchase = self.raw_value
if not purchase:
return ''
return tags.link_to(purchase, self.request.route_url('purchases.view', uuid=purchase.uuid))

View file

@ -1,82 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Simple Forms
"""
from __future__ import unicode_literals, absolute_import
from rattail.util import prettify
import pyramid_simpleform
from pyramid_simpleform import renderers
from webhelpers2.html import tags, HTML
from tailbone.forms import Form
class SimpleForm(Form):
"""
Customized simple form.
"""
def __init__(self, request, schema, obj=None, **kwargs):
super(SimpleForm, self).__init__(request, **kwargs)
self._form = pyramid_simpleform.Form(request, schema=schema, obj=obj)
def __getattr__(self, attr):
return getattr(self._form, attr)
def render(self, **kwargs):
kwargs['form'] = FormRenderer(self)
return super(SimpleForm, self).render(**kwargs)
def validate(self):
return self._form.validate()
class FormRenderer(renderers.FormRenderer):
"""
Customized form renderer. Provides some extra methods for convenience.
"""
def __getattr__(self, attr):
return getattr(self.form, attr)
def field_div(self, name, field, label=None):
errors = self.errors_for(name)
if errors:
errors = [HTML.tag('div', class_='field-error', c=x) for x in errors]
errors = tags.literal('').join(errors)
label = HTML.tag('label', for_=name, c=label or prettify(name))
inner = HTML.tag('div', class_='field', c=field)
outer_class = 'field-wrapper {}'.format(name)
if errors:
outer_class += ' error'
outer = HTML.tag('div', class_=outer_class, c=(errors or '') + label + inner)
return outer
def referrer_field(self):
return self.hidden('referrer', value=self.form.request.get_referrer())

View file

@ -1,153 +0,0 @@
# -*- 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 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/>.
#
################################################################################
"""
Custom Form Validators
"""
from __future__ import unicode_literals, absolute_import
import re
from rattail.db import model
from rattail.db.util import validate_email_address, validate_phone_number
from rattail.gpc import GPC
import formencode as fe
import formalchemy as fa
from tailbone.db import Session
class ValidGPC(fe.validators.FancyValidator):
"""
Validator for fields which should contain GPC value.
"""
def _to_python(self, value, state):
if value is not None:
digits = re.sub(r'\D', '', value)
if digits:
try:
return GPC(digits)
except ValueError as error:
raise fe.Invalid("Invalid UPC: {}".format(error), value, state)
def _from_python(self, upc, state):
if upc is None:
return ''
return upc.pretty()
class ModelValidator(fe.validators.FancyValidator):
"""
Generic validator for data model reference fields.
"""
model_class = None
@property
def model_name(self):
self.model_class.__name__
def _to_python(self, value, state):
if value:
obj = Session.query(self.model_class).get(value)
if obj:
return obj
raise fe.Invalid("{} not found".format(self.model_name), value, state)
def _from_python(self, value, state):
obj = value
if not obj:
return ''
return obj.uuid
def validate_python(self, value, state):
obj = value
if obj is not None and not isinstance(obj, self.model_class):
raise fe.Invalid("Value must be a valid {} object".format(self.model_name), value, state)
class ValidStore(ModelValidator):
"""
Validator for store field.
"""
model_class = model.Store
class ValidCustomer(ModelValidator):
"""
Validator for customer field.
"""
model_class = model.Customer
class ValidDepartment(ModelValidator):
"""
Validator for department field.
"""
model_class = model.Department
class ValidEmployee(ModelValidator):
"""
Validator for employee field.
"""
model_class = model.Employee
class ValidProduct(ModelValidator):
"""
Validator for product field.
"""
model_class = model.Product
class ValidUser(ModelValidator):
"""
Validator for user field.
"""
model_class = model.User
def valid_email_address(value, field=None):
"""
FormAlchemy-compatible validation function, which leverages FormEncode
under the hood.
"""
if value:
try:
return validate_email_address(value, error=True)
except Exception as error:
raise fa.ValidationError(unicode(error))
def valid_phone_number(value, field=None):
"""
FormAlchemy-compatible validation function, which leverages FormEncode
under the hood.
"""
if value:
try:
return validate_phone_number(value, error=True)
except Exception as error:
raise fa.ValidationError(unicode(error))

View file

@ -1,4 +0,0 @@
## -*- coding: utf-8 -*-
<%namespace file="/autocomplete.mako" import="autocomplete" />
${autocomplete(field_name, service_url, field_value, field_display, width=width, selected=selected, cleared=cleared)}

View file

@ -1,10 +0,0 @@
## -*- coding: utf-8 -*-
${h.text(name, value=value)}
<script type="text/javascript">
$(function() {
$('input[name="${name}"]').datepicker({
changeYear: ${'true' if change_year else 'false'},
dateFormat: 'yy-mm-dd'
});
});
</script>

View file

@ -1,40 +0,0 @@
## -*- coding: utf-8 -*-
<% _focus_rendered = False %>
% for error in fieldset.errors.get(None, []):
<div class="fieldset-error">${error}</div>
% endfor
% for field in fieldset.render_fields.itervalues():
% if field.requires_label:
<div class="field-wrapper ${field.name}">
% for error in field.errors:
<div class="field-error">${error}</div>
% endfor
${field.label_tag()|n}
<div class="field">${field.render()|n}</div>
% if 'instructions' in field.metadata:
<span class="instructions">${field.metadata['instructions']}</span>
% endif
</div>
% if not _focus_rendered and (fieldset.focus is True or fieldset.focus is field):
% if not field.is_readonly() and getattr(field.renderer, 'needs_focus', True):
<script type="text/javascript">
$(function() {
% if hasattr(field.renderer, 'focus_name'):
$('#${field.renderer.focus_name}').focus();
% else:
$('#${field.renderer.name}').focus();
% endif
});
</script>
<% _focus_rendered = True %>
% endif
% endif
% else:
${field.render()|n}
% endif
% endfor

View file

@ -1,7 +0,0 @@
## -*- coding: utf-8 -*-
<%namespace file="/forms/lib.mako" import="render_field_readonly" />
<div class="fieldset">
% for field in fieldset.render_fields.itervalues():
${render_field_readonly(field)}
% endfor
</div>

View file

@ -1,21 +0,0 @@
## -*- coding: utf-8 -*-
<div class="form">
${h.form(form.action_url, id=form.id or None, method='post', enctype='multipart/form-data')}
${form.csrf_token()}
${form.render_fields()|n}
% if buttons:
${buttons|n}
% else:
<div class="buttons">
${h.submit('create', form.create_label if form.creating else form.update_label)}
% if form.creating and form.allow_successive_creates:
${h.submit('create_and_continue', form.successive_create_label)}
% endif
<a href="${form.cancel_url}" class="button">Cancel</a>
</div>
% endif
${h.end_form()}
</div>

View file

@ -1,7 +0,0 @@
## -*- coding: utf-8 -*-
<div class="form">
${form.render_fields()|n}
% if buttons:
${buttons|n}
% endif
</div>

View file

@ -1,10 +0,0 @@
## -*- coding: utf-8 -*-
<%def name="render_field_readonly(field)">
% if field.requires_label:
<div class="field-wrapper ${field.name}">
${field.label_tag()|n}
<div class="field">${field.render_readonly()}</div>
</div>
% endif
</%def>