Browse Source

staffers can send out confirmation emails for signatures and payment

legal_entities
Christoph Scheid 8 years ago
parent
commit
f76af4ece9
12 changed files with 391 additions and 6 deletions
  1. +53
    -0
      alembic.ini
  2. +3
    -0
      alembic/README
  3. +73
    -0
      alembic/env.py
  4. +22
    -0
      alembic/script.py.mako
  5. +32
    -0
      alembic/versions/17343990ccc0_add_signature_and_paymant_confirmation_.py
  6. +2
    -0
      c3smembership/__init__.py
  7. +58
    -0
      c3smembership/accountants_views.py
  8. +90
    -0
      c3smembership/mail_utils.py
  9. +6
    -0
      c3smembership/models.py
  10. +32
    -6
      c3smembership/templates/dashboard.pt
  11. +19
    -0
      c3smembership/templates/detail.pt
  12. +1
    -0
      setup.py

+ 53
- 0
alembic.ini View File

@ -0,0 +1,53 @@
# A generic, single database configuration.
[alembic]
# path to migration scripts
script_location = alembic
# template used to generate migration files
# file_template = %%(rev)s_%%(slug)s
# max length of characters to apply to the
# "slug" field
#truncate_slug_length = 40
# set to 'true' to run the environment during
# the 'revision' command, regardless of autogenerate
# revision_environment = false
sqlalchemy.url = sqlite:///c3sMembership.db
# Logging configuration
[loggers]
keys = root,sqlalchemy,alembic
[handlers]
keys = console
[formatters]
keys = generic
[logger_root]
level = WARN
handlers = console
qualname =
[logger_sqlalchemy]
level = WARN
handlers =
qualname = sqlalchemy.engine
[logger_alembic]
level = INFO
handlers =
qualname = alembic
[handler_console]
class = StreamHandler
args = (sys.stderr,)
level = NOTSET
formatter = generic
[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S

+ 3
- 0
alembic/README View File

@ -0,0 +1,3 @@
Generic single-database configuration.
see https://alembic.readthedocs.org/en/latest/tutorial.html

+ 73
- 0
alembic/env.py View File

@ -0,0 +1,73 @@
from __future__ import with_statement
from alembic import context
from sqlalchemy import engine_from_config, pool
from logging.config import fileConfig
# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)
# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
from c3smembership.models import Base
target_metadata = Base.metadata
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.
def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well. By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(url=url)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
engine = engine_from_config(
config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
poolclass=pool.NullPool)
connection = engine.connect()
context.configure(
connection=connection,
target_metadata=target_metadata
)
try:
with context.begin_transaction():
context.run_migrations()
finally:
connection.close()
if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

+ 22
- 0
alembic/script.py.mako View File

@ -0,0 +1,22 @@
"""${message}
Revision ID: ${up_revision}
Revises: ${down_revision}
Create Date: ${create_date}
"""
# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}
from alembic import op
import sqlalchemy as sa
${imports if imports else ""}
def upgrade():
${upgrades if upgrades else "pass"}
def downgrade():
${downgrades if downgrades else "pass"}

+ 32
- 0
alembic/versions/17343990ccc0_add_signature_and_paymant_confirmation_.py View File

@ -0,0 +1,32 @@
"""add signature and paymant confirmation fields
Revision ID: 17343990ccc0
Revises: None
Create Date: 2013-12-11 14:01:32.099898
"""
# revision identifiers, used by Alembic.
revision = '17343990ccc0'
down_revision = None
from alembic import op
import sqlalchemy as sa
def upgrade():
### commands auto generated by Alembic - please adjust! ###
op.add_column('members', sa.Column('payment_confirmed', sa.Boolean(), nullable=True))
op.add_column('members', sa.Column('payment_confirmed_date', sa.DateTime(), nullable=True))
op.add_column('members', sa.Column('signature_confirmed', sa.Boolean(), nullable=True))
op.add_column('members', sa.Column('signature_confirmed_date', sa.DateTime(), nullable=True))
### end Alembic commands ###
def downgrade():
### commands auto generated by Alembic - please adjust! ###
op.drop_column('members', 'signature_confirmed_date')
op.drop_column('members', 'signature_confirmed')
op.drop_column('members', 'payment_confirmed_date')
op.drop_column('members', 'payment_confirmed')
### end Alembic commands ###

+ 2
- 0
c3smembership/__init__.py View File

@ -64,8 +64,10 @@ def main(global_config, **settings):
config.add_route('dashboard', '/dashboard/{number}')
config.add_route('detail', '/detail/{memberid}')
config.add_route('switch_sig', '/switch_sig/{memberid}')
config.add_route('mail_sig_confirmation', '/mail_sig_conf/{memberid}')
config.add_route('regenerate_pdf', '/re_C3S_SCE_AFM_{code}.pdf')
config.add_route('switch_pay', '/switch_pay/{memberid}')
config.add_route('mail_pay_confirmation', '/mail_pay_conf/{memberid}')
config.add_route('delete_entry', '/delete/{memberid}')
config.add_route('login', '/login')
config.add_route('logout', '/logout')


+ 58
- 0
c3smembership/accountants_views.py View File

@ -5,6 +5,10 @@ from c3smembership.models import (
C3sStaff,
)
from c3smembership.utils import generate_pdf
from c3smembership.mail_utils import (
make_signature_confirmation_emailbody,
make_payment_confirmation_emailbody
)
from pkg_resources import resource_filename
import colander
import deform
@ -21,6 +25,8 @@ from pyramid.security import (
forget,
authenticated_userid,
)
from pyramid_mailer import get_mailer
from pyramid_mailer.message import Message
from pyramid.url import route_url
from translationstring import TranslationStringFactory
@ -490,3 +496,55 @@ def regenerate_pdf(request):
}
return generate_pdf(_appstruct)
@view_config(permission='manage',
route_name='mail_sig_confirmation')
def mail_signature_confirmation(request):
"""
send a mail to membership applicant
informing her about reception of signature
"""
_id = request.matchdict['memberid']
_member = C3sMember.get_by_id(_id)
message = Message(
subject=_('[C3S AFM] We have received your signature. Thanks!'),
sender='yes@c3s.cc',
recipients=[_member.email],
body=make_signature_confirmation_emailbody(_member)
)
#print(message.body)
mailer = get_mailer(request)
mailer.send(message)
_member.signature_confirmed = True
_member.signature_confirmed_date = datetime.now()
return HTTPFound(request.route_url('dashboard',
number=request.cookies['on_page'])
)
@view_config(permission='manage',
route_name='mail_pay_confirmation')
def mail_payment_confirmation(request):
"""
send a mail to membership applicant
informing her about reception of payment
"""
_id = request.matchdict['memberid']
_member = C3sMember.get_by_id(_id)
message = Message(
subject=_('[C3S AFM] We have received your payment. Thanks!'),
sender='yes@c3s.cc',
recipients=[_member.email],
body=make_payment_confirmation_emailbody(_member)
)
#print(message.body)
mailer = get_mailer(request)
mailer.send(message)
_member.payment_confirmed = True
_member.payment_confirmed_date = datetime.now()
return HTTPFound(request.route_url('dashboard',
number=request.cookies['on_page'])
)

+ 90
- 0
c3smembership/mail_utils.py View File

@ -0,0 +1,90 @@
# -*- coding: utf-8 -*-
def make_payment_confirmation_emailbody(_input):
"""
a mail body to confirm reception of payment for shares
"""
_num_shares = _input.num_shares
_sum_shares = _num_shares * 50
if 'de' in _input.locale:
_body = (u"""Liebes Neumitglied,
Deine Überweisung für """ + str(_num_shares) +
u""" Anteile (""" + str(_sum_shares) +
u""" Euro) ist auf
unserem Konto eingegangen.
Falls Probleme aufgetreten sind, melde Dich bitte bei uns (yes@c3s.cc).
Danke für Deinen Beitrag zur C3S!
Liebe Grüße,
Das C3S-Team
"""
)
else:
_body = (u"""Dear new member,
Your transfer of """ + str(_sum_shares) + u" Euro for " + str(_num_shares) +
u""" shares just showed in our bank account.
In case of any problems please don't hesitate to contact us (yes@c3s.cc).
Thanks a lot for your contribution to the C3S!
Best wishes,
The C3S Team"""
)
return _body
def make_signature_confirmation_emailbody(_input):
"""
a mail body to confirm reception of signature
"""
_num_shares = _input.num_shares
_sum_shares = _num_shares * 50
if 'de' in _input.locale:
_body = (u"""Liebes Neumitglied,
Dein Beitrittsformular zur Zeichnung von """ +
str(_num_shares) +
u' Anteilen (' +
str(_sum_shares) + u""" Euro) ist sicher bei uns gelandet.
Falls Probleme aufgetreten sind, melde Dich bitte bei uns (yes@c3s.cc).
Schön, dass Du ein Teil der C3S werden möchtest!
Liebe Grüße,
Das C3S-Team
""")
else:
_body = (u"""Dear new member,
Your membership application form to sign """ +
str(_num_shares) +
u' shares (' +
str(_sum_shares) +
u""" Euro) safely arrived at our homebase.
In case of any problems please don't hesitate to contact us (yes@c3s.cc).
Great that you want to become a part of the C3S!
Best wishes,
The C3S Team
""")
return _body

+ 6
- 0
c3smembership/models.py View File

@ -172,9 +172,15 @@ class C3sMember(Base):
signature_received = Column(Boolean, default=False)
signature_received_date = Column(
DateTime(), default=datetime(1970, 1, 1))
signature_confirmed = Column(Boolean, default=False)
signature_confirmed_date = Column(
DateTime(), default=datetime(1970, 1, 1))
payment_received = Column(Boolean, default=False)
payment_received_date = Column(
DateTime(), default=datetime(1970, 1, 1))
payment_confirmed = Column(Boolean, default=False)
payment_confirmed_date = Column(
DateTime(), default=datetime(1970, 1, 1))
accountant_comment = Column(Unicode(255))
membership_type = Column(Unicode(255))
member_of_colsoc = Column(Boolean, default=False)


+ 32
- 6
c3smembership/templates/dashboard.pt View File

@ -42,8 +42,10 @@
<td>email</td>
<td>email<br />confirmed?</td>
<td>recreate<br />PDF</td>
<td>signature<br />received?</td>
<td>payment<br />received?</td>
<td>sig.<br />rec'd?</td>
<td>send sig.<br />conf. email</td>
<td>paym.<br />rec'd?</td>
<td>send paym.<br />conf. email</td>
<td># shares</td>
<td>edit</td>
<td>delete</td>
@ -58,28 +60,52 @@
<td><a href="/re_C3S_SCE_AFM_${member.email_confirm_code}.pdf">PDF</a></td>
<td>
<div tal:condition="not member.signature_received">
<a href="${request.route_url('switch_sig', memberid=member.id)}">
<a href="${request.route_url('switch_sig', memberid=member.id)}"
title="no signature received as of now. click to toggle...">
<img src="${request.static_url('c3smembership:static/flash_red.gif')}" width="20px" height="20px" />
</a>
</div>
<div tal:condition="member.signature_received">
<a href="${request.route_url('switch_sig', memberid=member.id)}">
<a href="${request.route_url('switch_sig', memberid=member.id)}"
title="signature received at ${member.signature_received_date}. click to unset...">
<img src="${request.static_url('c3smembership:static/green.png')}" width="20px" height="20px" />
</a>
</div>
</td>
<td>
<div tal:condition="not member.signature_confirmed">
<a href="${request.route_url('mail_sig_confirmation',
memberid=member.id)}">Mail!</a>
</div>
<div tal:condition="member.signature_confirmed">
<a href="#"
title="${member.signature_confirmed_date}">ok</a>
</div>
</td>
<td>
<div tal:condition="not member.payment_received">
<a href="${request.route_url('switch_pay', memberid=member.id)}">
<a href="${request.route_url('switch_pay', memberid=member.id)}"
title="no payment received as of now. click to toggle...">
<img src="${request.static_url('c3smembership:static/flash_red.gif')}" width="20px" height="20px" />
</a>
</div>
<div tal:condition="member.payment_received">
<a href="${request.route_url('switch_pay', memberid=member.id)}">
<a href="${request.route_url('switch_pay', memberid=member.id)}"
title="payment received at ${member.payment_received_date}. click to change: we have NOT received payment!">
<img src="${request.static_url('c3smembership:static/green.png')}" width="20px" height="20px" />
</a>
</div>
</td>
<td>
<div tal:condition="not member.payment_confirmed">
<a href="${request.route_url('mail_pay_confirmation',
memberid=member.id)}">Mail!</a>
</div>
<div tal:condition="member.payment_confirmed">
<a href="#"
title="${member.payment_confirmed_date}">ok</a>
</div>
</td>
<td>${member.num_shares}</td>
<td><a href="/detail/${member.id}">edit</a></td>
<td><a href="/delete/${member.id}">delete</a></td>


+ 19
- 0
c3smembership/templates/detail.pt View File

@ -56,6 +56,10 @@
<td>country</td>
<td>${member.country}</td>
</tr>
<tr>
<td>locale</td>
<td>${member.locale}</td>
</tr>
<tr>
<td>date_of_birth</td>
<td>${member.date_of_birth}</td>
@ -79,9 +83,24 @@
<tr>
<td>signature received?</td><td>${member.signature_received or "No"}</td>
</tr>
<tr tal:condition="member.signature_received">
<td>signature reception date</td><td>${member.signature_received_date}</td>
</tr>
<tr>
<td>signature confirmed?</td><td>${member.signature_confirmed or "No"}</td>
</tr>
<tr tal:condition="member.signature_confirmed">
<td>signature reception date</td><td>${member.signature_confirmed_date}</td>
</tr>
<tr>
<td>payment received?</td><td>${member.payment_received or "No"}</td>
</tr>
<tr tal:condition="member.payment_received">
<td>payment reception date</td><td>${member.payment_received_date}</td>
</tr>
<tr tal:condition="member.payment_confirmed">
<td>payment reception date</td><td>${member.payment_confirmed_date}</td>
</tr>
<tr>
<td># shares</td>
<td>${member.num_shares}</td>


+ 1
- 0
setup.py View File

@ -25,6 +25,7 @@ requires = [
'waitress',
'python-gnupg',
'unicodecsv',
'alembic', # migrate the database when introducing new fields
]
# for the translations machinery using transifex you also need to
# "pip install transifex-client"


Loading…
Cancel
Save