From d15c2149185ae787a8b2278156e665a6ff034efc Mon Sep 17 00:00:00 2001
From: Mike Pagel
Date: Wed, 13 Sep 2017 07:09:44 +0200
Subject: [PATCH 1/8] fixed unset original article in new article form by using
service API in full
---
bizwiz/invoices/views.py | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/bizwiz/invoices/views.py b/bizwiz/invoices/views.py
index 64cf6f5..65bc059 100644
--- a/bizwiz/invoices/views.py
+++ b/bizwiz/invoices/views.py
@@ -285,14 +285,22 @@ def forms_valid(self, invoice_form, invoiced_article_formset):
rebates = invoice_form.cleaned_data['rebates']
with transaction.atomic():
+ invoiced_articles = invoiced_article_formset.save(commit=False)
invoice = services.create_invoice(
project=project,
+ invoiced_articles=invoiced_articles,
customer=original_customer,
rebates=rebates,
)
- invoiced_article_formset.instance = invoice
- invoiced_article_formset.save()
- services.refresh_rebates(invoice)
+
+ # DEFECT:
+ # original article is not set this way
+ # --> incomplete data
+ # --> incorrect article sales report
+ # TODO: try to make use of full create_invoice API, do not take shortcut of formset
+ # TODO: clean up data by reading article references via name comparison
+ # TODO: implement report based on invoiced article names, not from original one
+ # TODO: remove "original" references (article and customer) if possible
return self.form_valid(invoice_form)
From b864c63d899edfe3abee4473be2c995f0d2b401d Mon Sep 17 00:00:00 2001
From: Mike Pagel
Date: Wed, 13 Sep 2017 07:22:22 +0200
Subject: [PATCH 2/8] based query of sold articles on invoiced article names
instead of original article names
---
bizwiz/invoices/views.py | 13 ++++---------
1 file changed, 4 insertions(+), 9 deletions(-)
diff --git a/bizwiz/invoices/views.py b/bizwiz/invoices/views.py
index 65bc059..1a5245d 100644
--- a/bizwiz/invoices/views.py
+++ b/bizwiz/invoices/views.py
@@ -361,12 +361,7 @@ def get_context_data(self, **kwargs):
class ArticleSalesTable(tables.Table):
- name = tables.LinkColumn(
- 'articles:update',
- args=[tables.utils.A('original_article__pk')],
- verbose_name=_("Invoice text"),
- accessor='original_article__name'
- )
+ name = tables.Column(_("Invoice text"))
amount = tables.Column(_("Ordered"), accessor='year_amount', attrs=COLUMN_RIGHT_ALIGNED)
total = tables.Column(_('Total value'), attrs=COLUMN_RIGHT_ALIGNED)
@@ -384,10 +379,10 @@ class ArticleSales(mixins.LoginRequiredMixin, SizedColumnsMixin, tables.SingleTa
def get_queryset(self):
return InvoicedArticle.objects \
- .filter(kind=ItemKind.ARTICLE, original_article__isnull=False) \
+ .filter(kind=ItemKind.ARTICLE) \
.filter(price__gt=0) \
.filter(invoice__date_paid__year=self.kwargs['year']) \
- .values('original_article__pk', 'original_article__name') \
+ .values('name') \
.annotate(year_amount=models.Sum('amount'),
total=models.Sum(models.F('price') * models.F('amount'))) \
.order_by('-year_amount')
@@ -398,7 +393,7 @@ def get_context_data(self, **kwargs):
# prepare chart data by separating x and y axis:
queryset = context['object_list']
article_amounts = [a['year_amount'] for a in queryset]
- article_names = [a['original_article__name'] for a in queryset]
+ article_names = [a['name'] for a in queryset]
# clear names of articles with minor contribution:
total_amount = sum(article_amounts)
From b1c266b817fe861310437a30333bfc98c13d1bdb Mon Sep 17 00:00:00 2001
From: Mike Pagel
Date: Wed, 13 Sep 2017 20:06:00 +0200
Subject: [PATCH 3/8] removed original article and customer
---
bizwiz/invoices/forms.py | 4 +--
.../migrations/0014_auto_20170913_1944.py | 23 ++++++++++++++
bizwiz/invoices/models.py | 20 ++-----------
bizwiz/invoices/services.py | 7 -----
bizwiz/invoices/views.py | 11 ++-----
test/bizwiz/invoices/test_models.py | 2 --
test/bizwiz/invoices/test_services.py | 13 --------
test/bizwiz/invoices/test_views.py | 30 +++++--------------
8 files changed, 37 insertions(+), 73 deletions(-)
create mode 100644 bizwiz/invoices/migrations/0014_auto_20170913_1944.py
diff --git a/bizwiz/invoices/forms.py b/bizwiz/invoices/forms.py
index f500655..46bcfee 100644
--- a/bizwiz/invoices/forms.py
+++ b/bizwiz/invoices/forms.py
@@ -170,7 +170,7 @@ class Meta:
class InvoicedCustomerForm(forms.ModelForm):
class Meta:
model = InvoicedCustomer
- exclude = ('original_customer', 'invoice')
+ exclude = ('invoice', )
helper = helper.FormHelper()
helper.form_tag = False
@@ -253,7 +253,7 @@ class BaseInvoicedArticleFormset(forms.BaseInlineFormSet):
min_num=1,
validate_min=True,
extra=0,
- exclude=('original_article', 'invoice')
+ exclude=('invoice', )
)
diff --git a/bizwiz/invoices/migrations/0014_auto_20170913_1944.py b/bizwiz/invoices/migrations/0014_auto_20170913_1944.py
new file mode 100644
index 0000000..ca9c409
--- /dev/null
+++ b/bizwiz/invoices/migrations/0014_auto_20170913_1944.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# Generated by Django 1.11.4 on 2017-09-13 17:44
+from __future__ import unicode_literals
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('invoices', '0013_auto_20170903_1541'),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name='invoicedarticle',
+ name='original_article',
+ ),
+ migrations.RemoveField(
+ model_name='invoicedcustomer',
+ name='original_customer',
+ ),
+ ]
diff --git a/bizwiz/invoices/models.py b/bizwiz/invoices/models.py
index 30e1847..5867214 100644
--- a/bizwiz/invoices/models.py
+++ b/bizwiz/invoices/models.py
@@ -78,14 +78,6 @@ class InvoicedArticle(ArticleBase):
default=ItemKind.ARTICLE,
blank=True,
)
- original_article = models.ForeignKey(
- Article,
- on_delete=models.SET_NULL,
- verbose_name=_("Original article"),
- blank=True,
- null=True,
- related_name='invoiced_articles',
- )
amount = models.PositiveSmallIntegerField(_("Amount"))
invoice = models.ForeignKey(
Invoice,
@@ -100,7 +92,7 @@ class Meta:
@classmethod
def create(cls, invoice, article, amount):
- invoiced_article = InvoicedArticle(invoice=invoice, original_article=article, amount=amount)
+ invoiced_article = InvoicedArticle(invoice=invoice, amount=amount)
if article:
copy_field_data(ArticleBase, article, invoiced_article)
return invoiced_article
@@ -115,14 +107,6 @@ def total(self):
class InvoicedCustomer(CustomerBase):
- original_customer = models.ForeignKey(
- Customer,
- on_delete=models.SET_NULL,
- verbose_name=_("Original customer"),
- blank=True,
- null=True,
- related_name='invoiced_customer',
- )
invoice = models.OneToOneField(
Invoice,
verbose_name=_("Invoice"),
@@ -136,6 +120,6 @@ class Meta:
@classmethod
def create(cls, invoice, customer):
- invoiced_customer = InvoicedCustomer(invoice=invoice, original_customer=customer)
+ invoiced_customer = InvoicedCustomer(invoice=invoice)
copy_field_data(CustomerBase, customer, invoiced_customer)
return invoiced_customer
diff --git a/bizwiz/invoices/services.py b/bizwiz/invoices/services.py
index 69362c9..3059841 100644
--- a/bizwiz/invoices/services.py
+++ b/bizwiz/invoices/services.py
@@ -81,15 +81,8 @@ def create_invoice(*, customer, invoiced_articles=None, project=None, rebates=No
_logger.info('Creating new invoice {} for customer {}.'.format(invoice.number, customer))
if invoiced_articles:
- # get articles from database by names:
- names = {a.name for a in invoiced_articles}
- articles = Article.objects.filter(name__in=names)
- article_by_name = {a.name: a for a in articles}
-
for invoiced_article in invoiced_articles:
- original_article = article_by_name.get(invoiced_article.name)
invoiced_article.invoice = invoice
- invoiced_article.original_article = original_article
invoiced_article.save()
_logger.debug(' {}'.format(invoiced_article))
diff --git a/bizwiz/invoices/views.py b/bizwiz/invoices/views.py
index 1a5245d..b43100e 100644
--- a/bizwiz/invoices/views.py
+++ b/bizwiz/invoices/views.py
@@ -293,15 +293,6 @@ def forms_valid(self, invoice_form, invoiced_article_formset):
rebates=rebates,
)
- # DEFECT:
- # original article is not set this way
- # --> incomplete data
- # --> incorrect article sales report
- # TODO: try to make use of full create_invoice API, do not take shortcut of formset
- # TODO: clean up data by reading article references via name comparison
- # TODO: implement report based on invoiced article names, not from original one
- # TODO: remove "original" references (article and customer) if possible
-
return self.form_valid(invoice_form)
@@ -338,6 +329,8 @@ class Sales(mixins.LoginRequiredMixin, SizedColumnsMixin, tables.SingleTableView
"""Sales per year."""
table_class = SalesTable
column_widths = ('10%', '20%', '30%', '40%',)
+
+ # TODO correct query to show yearly sum including rebates
queryset = Invoice.objects \
.exclude(date_paid=None) \
.annotate(year_paid=functions.ExtractYear('date_paid')) \
diff --git a/test/bizwiz/invoices/test_models.py b/test/bizwiz/invoices/test_models.py
index 5c628eb..3bc96c7 100644
--- a/test/bizwiz/invoices/test_models.py
+++ b/test/bizwiz/invoices/test_models.py
@@ -31,7 +31,6 @@ def test__invoiced_customer__create():
assert c2.company_name == 'COMPANY_NAME'
assert c2.zip_code == 'ZIP'
assert c2.city == 'CITY'
- assert c2.original_customer == c1
def test__invoiced_article__create():
@@ -48,7 +47,6 @@ def test__invoiced_article__create():
assert a2.name == 'NAME'
assert a2.price == 1.2
assert a2.amount == 1
- assert a2.original_article == a1
assert a2.kind == ItemKind.ARTICLE
diff --git a/test/bizwiz/invoices/test_services.py b/test/bizwiz/invoices/test_services.py
index 9649eb7..40694ee 100644
--- a/test/bizwiz/invoices/test_services.py
+++ b/test/bizwiz/invoices/test_services.py
@@ -92,7 +92,6 @@ def test__create_invoice__global(customer, posted_articles):
invoice = create_invoice(customer=customer, invoiced_articles=posted_articles)
assert not invoice.project
- assert invoice.invoiced_customer.original_customer == customer
invoiced_articles = list(invoice.invoiced_articles.all())
assert len(invoiced_articles) == 2
assert invoiced_articles[0].name == 'A1'
@@ -123,18 +122,6 @@ def test__create_invoice__number_unique(customer, posted_articles):
assert invoice.number not in {'123', '45'}
-@pytest.mark.django_db
-def test__create_invoice__original_article(customer, posted_articles):
- article = Article(name='A1', price=5)
- article.save()
-
- invoice = create_invoice(customer=customer, invoiced_articles=posted_articles)
-
- invoiced_articles = invoice.invoiced_articles.all()
- assert invoiced_articles[0].original_article == article
- assert invoiced_articles[1].original_article is None
-
-
@pytest.fixture
def invoice_with_rebates(customer, posted_articles):
rebate1 = Rebate(name='NAME1', kind=RebateKind.PERCENTAGE, value=10, auto_apply=False)
diff --git a/test/bizwiz/invoices/test_views.py b/test/bizwiz/invoices/test_views.py
index 0266114..9dfc093 100644
--- a/test/bizwiz/invoices/test_views.py
+++ b/test/bizwiz/invoices/test_views.py
@@ -72,13 +72,6 @@ def test__sales__queryset__empty():
@pytest.fixture
def invoices_for_aggregation():
- articles = [
- Article(name='A', price=1),
- Article(name='B', price=2),
- ]
- for a in articles:
- a.save()
-
invoices = [
Invoice(number=1),
Invoice(number=2, date_paid=datetime.date(2016, 1, 2)),
@@ -91,46 +84,41 @@ def invoices_for_aggregation():
invoice_articles1 = [
InvoicedArticle(
invoice=invoices[0],
- name='A1',
+ name='A',
price=1,
amount=1,
- original_article=articles[0],
kind=ItemKind.ARTICLE,
),
]
invoiced_articles2 = [
InvoicedArticle(
invoice=invoices[1],
- name='B1',
+ name='A',
price=10.03,
amount=5,
- original_article=articles[0],
kind=ItemKind.ARTICLE,
),
InvoicedArticle(
invoice=invoices[1],
- name='B2',
+ name='B',
price=11,
amount=6,
- original_article=articles[1],
kind=ItemKind.ARTICLE,
),
]
invoiced_articles3 = [
InvoicedArticle(
invoice=invoices[2],
- name='C1',
+ name='A',
price=100,
amount=10,
- original_article=articles[0],
kind=ItemKind.ARTICLE,
),
InvoicedArticle(
invoice=invoices[2],
- name='C2',
+ name='B',
price=101,
amount=11,
- original_article=articles[1],
kind=ItemKind.ARTICLE,
),
InvoicedArticle(
@@ -144,18 +132,16 @@ def invoices_for_aggregation():
invoiced_articles4 = [
InvoicedArticle(
invoice=invoices[3],
- name='D1',
+ name='A',
price=1000,
amount=20,
- original_article=articles[0],
kind=ItemKind.ARTICLE,
),
InvoicedArticle(
invoice=invoices[3],
- name='D2',
+ name='B',
price=1001,
amount=21,
- original_article=articles[1],
kind=ItemKind.ARTICLE,
),
]
@@ -191,7 +177,7 @@ def test__article_sales__queryset__computation(invoices_for_aggregation):
sales.kwargs = dict(year=2016)
qs = sales.get_queryset()
- sales_by_article_name = {s['original_article__name']: s for s in qs}
+ sales_by_article_name = {s['name']: s for s in qs}
assert len(sales_by_article_name) == 2
From 5e63ad329ea5ffe45fc0d430a114805a624afb3a Mon Sep 17 00:00:00 2001
From: Mike Pagel
Date: Thu, 14 Sep 2017 07:23:22 +0200
Subject: [PATCH 4/8] fixed aggregation in sales
---
bizwiz/invoices/views.py | 15 ++++++++++-----
test/bizwiz/invoices/test_views.py | 2 +-
2 files changed, 11 insertions(+), 6 deletions(-)
diff --git a/bizwiz/invoices/views.py b/bizwiz/invoices/views.py
index b43100e..dd7e163 100644
--- a/bizwiz/invoices/views.py
+++ b/bizwiz/invoices/views.py
@@ -330,16 +330,21 @@ class Sales(mixins.LoginRequiredMixin, SizedColumnsMixin, tables.SingleTableView
table_class = SalesTable
column_widths = ('10%', '20%', '30%', '40%',)
- # TODO correct query to show yearly sum including rebates
queryset = Invoice.objects \
.exclude(date_paid=None) \
.annotate(year_paid=functions.ExtractYear('date_paid')) \
.values('year_paid') \
- .filter(invoiced_articles__kind=ItemKind.ARTICLE) \
- .annotate(num_invoices=models.Count('id', distinct=True),
- num_articles=models.Sum('invoiced_articles__amount'),
- total=models.Sum(
+ .annotate(total=models.Sum(
models.F('invoiced_articles__price') * models.F('invoiced_articles__amount')
+ )) \
+ .annotate(num_invoices=models.Count('id', distinct=True),
+ num_articles=models.Sum(
+ models.Case(
+ models.When(invoiced_articles__kind=ItemKind.ARTICLE,
+ invoiced_articles__price__gt=0,
+ then='invoiced_articles__amount'),
+ default=0
+ )
))
template_name = 'invoices/sales_list.html'
diff --git a/test/bizwiz/invoices/test_views.py b/test/bizwiz/invoices/test_views.py
index 9dfc093..faf32a3 100644
--- a/test/bizwiz/invoices/test_views.py
+++ b/test/bizwiz/invoices/test_views.py
@@ -168,7 +168,7 @@ def test__sales__queryset__computation(invoices_for_aggregation):
assert sales_2016['year_paid'] == 2016
assert sales_2016['num_invoices'] == 2
assert sales_2016['num_articles'] == 11 + 10 + 6 + 5
- assert sales_2016['total'] == decimal.Decimal('2227.15')
+ assert sales_2016['total'] == decimal.Decimal('2217.15')
@pytest.mark.django_db
From 69e36680fc2b1fd8db5be8c8b9dbd8c90d3fa047 Mon Sep 17 00:00:00 2001
From: Mike Pagel
Date: Thu, 14 Sep 2017 18:03:25 +0200
Subject: [PATCH 5/8] documented changes of new version
---
CHANGELOG.md | 5 +++++
1 file changed, 5 insertions(+)
create mode 100644 CHANGELOG.md
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..9e7a3cc
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,5 @@
+# 4.3.1
+
+Various sales report fixes and improvements:
+* Annual income now exact sum of all invoices.
+* Drilldown to articles sold in given year now shows article names from invoice, not the originally selected article (from master data).
From 1c16a5a46fed7f4be68dd5dde5b9692805c96347 Mon Sep 17 00:00:00 2001
From: Mike Pagel
Date: Thu, 14 Sep 2017 18:19:07 +0200
Subject: [PATCH 6/8] basic changelog display logic
---
bizwiz/common/templates/common/welcome.html | 21 ++-------------------
bizwiz/common/views.py | 21 ++++++++++++++++++++-
2 files changed, 22 insertions(+), 20 deletions(-)
diff --git a/bizwiz/common/templates/common/welcome.html b/bizwiz/common/templates/common/welcome.html
index 596b7ad..91af7fe 100644
--- a/bizwiz/common/templates/common/welcome.html
+++ b/bizwiz/common/templates/common/welcome.html
@@ -12,25 +12,8 @@ {% trans 'Welcome to Bizwiz 4' %}
aspiring photographer.
{% endblocktrans %}
- {% trans 'Background' %}
-
- {% blocktrans %}
- With the official start of the photography business at
- http://www.bpfotografie.de/, Bizwiz started in early 2007 as a Windows
- forms application based on the .NET framework. The first version used in production
- was Bizwiz 2 and was written in C# and used MSSQL as a server backend.
- {% endblocktrans %}
-
-
- {% blocktrans %}
- After production use until end of 2016, Bizwiz was fully rewritten as a Django web
- application, utilizing Python as well as the typical web languages. Bizwiz is still
- a server-rendered (classic) web application, as it is truly data-driven and the
- classic render-edit-post cycle applies very well. In addition, learning the classic
- Django framework was also an objective of the project. Today, Bizwiz is running
- within a Docker container on virtually any platform.
- {% endblocktrans %}
-
+ {% trans 'Changelog' %}
+ {{ changelog | safe }}
{% trans 'Resources' %}
{% blocktrans %}
diff --git a/bizwiz/common/views.py b/bizwiz/common/views.py
index c03f355..a1eaf0f 100644
--- a/bizwiz/common/views.py
+++ b/bizwiz/common/views.py
@@ -1,14 +1,33 @@
+import logging
+import os
+
+from django import conf
from django.views import generic
from bizwiz.common.session_filter import set_session_filter
from bizwiz.version import BIZWIZ_VERSION
+_logger = logging.getLogger(__name__)
+
class Welcome(generic.TemplateView):
template_name = 'common/welcome.html'
def get_context_data(self, **kwargs):
- return super().get_context_data(version=BIZWIZ_VERSION, **kwargs)
+
+ # read an render changelog markdown:
+ try:
+ with open(os.path.join(conf.settings.BASE_DIR, 'CHANGELOG.md')) as changelogfile:
+ changelog = changelogfile.read()
+ except FileNotFoundError:
+ _logger.warning('Changelog not found.')
+ changelog = ''
+
+ return super().get_context_data(
+ version=BIZWIZ_VERSION,
+ changelog=changelog,
+ **kwargs
+ )
class SizedColumnsMixin:
From ab9b0ed784c2f1b82da3b1c419dc0e088ba68df8 Mon Sep 17 00:00:00 2001
From: Mike Pagel
Date: Thu, 14 Sep 2017 19:36:50 +0200
Subject: [PATCH 7/8] added display of changelog
---
CHANGELOG.md | 1 +
bizwiz/common/templates/common/changelog.html | 5 +++++
bizwiz/common/templates/common/welcome.html | 6 +-----
bizwiz/common/urls.py | 1 +
bizwiz/common/views.py | 14 ++++++++++++--
requirements.txt | 3 +++
6 files changed, 23 insertions(+), 7 deletions(-)
create mode 100644 bizwiz/common/templates/common/changelog.html
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9e7a3cc..044f048 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
# 4.3.1
Various sales report fixes and improvements:
+
* Annual income now exact sum of all invoices.
* Drilldown to articles sold in given year now shows article names from invoice, not the originally selected article (from master data).
diff --git a/bizwiz/common/templates/common/changelog.html b/bizwiz/common/templates/common/changelog.html
new file mode 100644
index 0000000..65a5662
--- /dev/null
+++ b/bizwiz/common/templates/common/changelog.html
@@ -0,0 +1,5 @@
+{% extends "common/base.html" %}
+
+{% block content %}
+ {{ changelog | safe }}
+{% endblock content %}
diff --git a/bizwiz/common/templates/common/welcome.html b/bizwiz/common/templates/common/welcome.html
index 91af7fe..d85dfc2 100644
--- a/bizwiz/common/templates/common/welcome.html
+++ b/bizwiz/common/templates/common/welcome.html
@@ -12,8 +12,6 @@ {% trans 'Welcome to Bizwiz 4' %}
aspiring photographer.
{% endblocktrans %}
- {% trans 'Changelog' %}
- {{ changelog | safe }}
{% trans 'Resources' %}
{% blocktrans %}
@@ -23,8 +21,6 @@
{% trans 'Resources' %}
https://hub.docker.com/r/mpagel/bizwiz.
{% endblocktrans %}
-
- Version: {{ version }}
-
+ Version: {{ version }}
{% endblock content %}
diff --git a/bizwiz/common/urls.py b/bizwiz/common/urls.py
index 757015d..a14bfc7 100644
--- a/bizwiz/common/urls.py
+++ b/bizwiz/common/urls.py
@@ -6,5 +6,6 @@
urlpatterns = [
url(r'^$', views.Welcome.as_view(), name='index'),
+ url(r'^changelog$', views.Changelog.as_view(), name='changelog'),
url(r'^session-filter/$', views.SessionFilter.as_view(), name='session-filter'),
]
diff --git a/bizwiz/common/views.py b/bizwiz/common/views.py
index a1eaf0f..cb52b62 100644
--- a/bizwiz/common/views.py
+++ b/bizwiz/common/views.py
@@ -3,6 +3,7 @@
from django import conf
from django.views import generic
+import markdown
from bizwiz.common.session_filter import set_session_filter
from bizwiz.version import BIZWIZ_VERSION
@@ -13,18 +14,27 @@
class Welcome(generic.TemplateView):
template_name = 'common/welcome.html'
+ def get_context_data(self, **kwargs):
+ return super().get_context_data(
+ version=BIZWIZ_VERSION,
+ **kwargs
+ )
+
+
+class Changelog(generic.TemplateView):
+ template_name = 'common/changelog.html'
+
def get_context_data(self, **kwargs):
# read an render changelog markdown:
try:
with open(os.path.join(conf.settings.BASE_DIR, 'CHANGELOG.md')) as changelogfile:
- changelog = changelogfile.read()
+ changelog = markdown.markdown(changelogfile.read())
except FileNotFoundError:
_logger.warning('Changelog not found.')
changelog = ''
return super().get_context_data(
- version=BIZWIZ_VERSION,
changelog=changelog,
**kwargs
)
diff --git a/requirements.txt b/requirements.txt
index 6aac9d6..32c0861 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -21,6 +21,9 @@ pytest-django
# PDF generation
reportlab
+# markdown to html (e.g. for changelog display)
+markdown
+
# serve static files simply via gunicorn
whitenoise
From d4bd91af80624e1de0293f085267bfa25f1c6bb0 Mon Sep 17 00:00:00 2001
From: Mike Pagel
Date: Thu, 14 Sep 2017 19:37:35 +0200
Subject: [PATCH 8/8] added markdown to production package list
---
requirements-prod.txt | 1 +
1 file changed, 1 insertion(+)
diff --git a/requirements-prod.txt b/requirements-prod.txt
index 5931a67..6031c19 100644
--- a/requirements-prod.txt
+++ b/requirements-prod.txt
@@ -5,6 +5,7 @@ django-crispy-forms==1.6.1
django-jquery-js==3.1.1
django-tables2==1.10.0
gunicorn==19.7.1
+Markdown==2.6.9
olefile==0.44
Pillow==4.2.1
py==1.4.34