diff --git a/main/forms.py b/main/forms.py index 19902eb..a462221 100644 --- a/main/forms.py +++ b/main/forms.py @@ -1,6 +1,5 @@ # coding: utf-8 from django import forms -from django.contrib.auth.models import User from django.utils.translation import ugettext as _ from models import * @@ -9,9 +8,20 @@ import utils class OrderForm(forms.ModelForm): + customer_name = forms.CharField(required=False) + + def clean(self): + if self.cleaned_data['customer'] is None and self.cleaned_data['customer_name'] != u"Ekstern": + raise forms.ValidationError(_(u"Du har ikke valgt hvem som skal kjøpe.")) + + return self.cleaned_data + def clean_customer(self): + if self.cleaned_data['customer'] is None: + return self.cleaned_data['customer'] + if self.cleaned_data['customer'].get_profile().balance <= 0: - raise forms.ValidationError(_("Du har negativ saldo og kan ikke kjøpe noe.")) + raise forms.ValidationError(_(u"Du har negativ saldo og kan ikke kjøpe noe.")) return self.cleaned_data['customer'] @@ -23,7 +33,8 @@ class OrderForm(forms.ModelForm): class OrderLineForm(forms.Form): product = forms.IntegerField(required=True) amount = forms.IntegerField(required=True) - unit_price = forms.FloatField(required=True, localize=True) + unit_price_int = forms.FloatField(required=True, localize=True) + unit_price_ext = forms.FloatField(required=True, localize=True) class DepositForm(forms.ModelForm): @@ -38,10 +49,10 @@ class SimpleCreateUserForm(forms.Form): email = forms.EmailField(required=True, label="E-post") facebook_username = forms.CharField(required=False, label="Facebook-brukernavn") - def _get_facebook_url(self, facebook_username): - if facebook_username == "": + def _get_facebook_url(self): + if self.cleaned_data['facebook_username'] == "": return "" - return "https://graph.facebook.com/{0}/picture".format(facebook_username) + return "https://graph.facebook.com/{0}/picture".format(self.cleaned_data['facebook_username']) def save(self, commit=True): username = utils.format_username(self.cleaned_data['first_name'], self.cleaned_data['last_name']) @@ -50,7 +61,7 @@ class SimpleCreateUserForm(forms.Form): user.last_name = self.cleaned_data['last_name'] profile = user.get_profile() - profile.image = self._get_facebook_url(self.cleaned_data['facebook_username']) + profile.image = self._get_facebook_url() if commit: user.save() @@ -64,7 +75,7 @@ class InventoryTransactionForm(forms.ModelForm): def __init__(self, *args, **kwargs): # Only show active products - super (InventoryTransactionForm, self).__init__(*args,**kwargs) + super(InventoryTransactionForm, self).__init__(*args, **kwargs) self.fields['product'].queryset = Product.objects.filter(active=True) class Meta: @@ -75,4 +86,3 @@ class InventoryTransactionForm(forms.ModelForm): class DateRangeForm(forms.Form): start_time = forms.DateTimeField(required=False, label=_("Fra")) end_time = forms.DateTimeField(required=False, label=_("Til")) - diff --git a/main/migrations/0003_auto__chg_field_order_customer.py b/main/migrations/0003_auto__chg_field_order_customer.py new file mode 100644 index 0000000..131f35d --- /dev/null +++ b/main/migrations/0003_auto__chg_field_order_customer.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Changing field 'Order.customer' + db.alter_column(u'main_order', 'customer_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'], null=True)) + + def backwards(self, orm): + + # User chose to not deal with backwards NULL issues for 'Order.customer' + raise RuntimeError("Cannot reverse this migration. 'Order.customer' and its values cannot be restored.") + + # The following code is provided here to aid in writing a correct migration + # Changing field 'Order.customer' + db.alter_column(u'main_order', 'customer_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['auth.User'])) + + models = { + u'auth.group': { + 'Meta': {'object_name': 'Group'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + u'auth.permission': { + 'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + u'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}), + 'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'}) + }, + u'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + u'main.inventorytransaction': { + 'Meta': {'object_name': 'InventoryTransaction'}, + 'amount': ('django.db.models.fields.FloatField', [], {}), + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'product': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transactions'", 'to': u"orm['main.Product']"}), + 'unit_price': ('django.db.models.fields.FloatField', [], {}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + u'main.order': { + 'Meta': {'object_name': 'Order'}, + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'customer': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']", 'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order_sum': ('django.db.models.fields.FloatField', [], {'db_column': "'sum'"}) + }, + u'main.orderline': { + 'Meta': {'unique_together': "(('order', 'product'),)", 'object_name': 'OrderLine'}, + 'amount': ('django.db.models.fields.IntegerField', [], {}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'order': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['main.Order']"}), + 'product': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'orderlines'", 'to': u"orm['main.Product']"}), + 'unit_price': ('django.db.models.fields.FloatField', [], {}) + }, + u'main.product': { + 'Meta': {'ordering': "['name']", 'object_name': 'Product'}, + 'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'alcohol_percent': ('django.db.models.fields.FloatField', [], {'null': 'True'}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'customer_support': ('django.db.models.fields.CharField', [], {'max_length': '32', 'blank': 'True'}), + 'description': ('django.db.models.fields.CharField', [], {'max_length': '256', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '100', 'blank': 'True'}), + 'inventory_amount': ('django.db.models.fields.FloatField', [], {'default': '0'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'sale_price_ext': ('django.db.models.fields.FloatField', [], {}), + 'sale_price_int': ('django.db.models.fields.FloatField', [], {}), + 'updated': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}), + 'volume_liter': ('django.db.models.fields.FloatField', [], {'null': 'True'}) + }, + u'main.transaction': { + 'Meta': {'object_name': 'Transaction'}, + 'amount': ('django.db.models.fields.FloatField', [], {}), + 'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['auth.User']"}) + }, + u'main.userprofile': { + 'Meta': {'object_name': 'UserProfile'}, + 'balance': ('django.db.models.fields.FloatField', [], {'default': '0.0'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'image': ('django.db.models.fields.URLField', [], {'max_length': '200', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['auth.User']", 'unique': 'True'}) + } + } + + complete_apps = ['main'] \ No newline at end of file diff --git a/main/models.py b/main/models.py index 698076b..fa5235e 100755 --- a/main/models.py +++ b/main/models.py @@ -25,20 +25,40 @@ class Product(models.Model): updated = models.DateTimeField(auto_now=True) @property - def inventory_value(self): - return self.inventory_amount * self.sale_price_int + def wholesale_unit_price(self): + """ + This is not accurate. + """ + if self.inventory_amount <= 0: + return 0 + + unit_prices = self.transactions.order_by('created').values('amount', 'unit_price') + sum_units = 0 + for up in unit_prices: + sum_units += up['amount'] + if self.inventory_amount <= sum_units: + return up['unit_price'] + + return unit_prices[0]['unit_price'] + + @property + def wholesale_value(self): + return self.wholesale_unit_price * self.inventory_amount class Meta: ordering = ['name'] class Order(models.Model): - customer = models.ForeignKey(User) + customer = models.ForeignKey(User, null=True, blank=True) order_sum = models.FloatField(db_column='sum') created = models.DateTimeField(auto_now_add=True) + def is_external(self): + return self.customer is None + def __unicode__(self): - return u"{0}: {1} kr".format(self.customer, self.order_sum) + return u"{0}: {1} kr".format(self.customer or u"Ekstern", self.order_sum) class OrderLine(models.Model): diff --git a/main/views.py b/main/views.py index ff2a7fd..4848e09 100755 --- a/main/views.py +++ b/main/views.py @@ -51,45 +51,64 @@ def register(request): formset = OrderLineFormSet(request.POST) if formset.is_valid() and orderform.is_valid(): - order_sum = sum([ol['amount'] * ol['unit_price'] for ol in formset.cleaned_data]) order = orderform.save(commit=False) - # can he afford it? - profile = order.customer.get_profile() - if order_sum > profile.balance: - messages.error(request, u'{0} {1} har ikke råd, mangler {2} kr.'.format( - order.customer.first_name, - order.customer.last_name, - int(order_sum - profile.balance))) - return HttpResponseRedirect(reverse('main.views.register')) + + if order.is_external(): + order_sum = sum([ol['amount'] * ol['unit_price_ext'] for ol in formset.cleaned_data]) + else: + order_sum = sum([ol['amount'] * ol['unit_price_int'] for ol in formset.cleaned_data]) + # empty order? if order_sum == 0: messages.error(request, u'Du har ikke valgt hva du skal kjøpe...') return HttpResponseRedirect(reverse('main.views.register')) + + if not order.is_external(): + profile = order.customer.get_profile() + # can she afford it? + if order_sum > profile.balance: + messages.error(request, u'{0} {1} har ikke råd, mangler {2} kr.'.format( + order.customer.first_name, + order.customer.last_name, + int(order_sum - profile.balance))) + return HttpResponseRedirect(reverse('main.views.register')) + # substract order from balance + profile.balance -= order.order_sum + profile.save() + order.order_sum = order_sum order.save() - # substract order from balance - profile.balance -= order.order_sum - profile.save() orderlines = [] for ol in formset.cleaned_data: if ol['amount'] > 0: product = Product.objects.get(pk=ol['product']) + + if order.is_external(): + unit_price = ol['unit_price_ext'] + else: + unit_price = ol['unit_price_int'] + OrderLine.objects.create( order=order, product=product, amount=ol['amount'], - unit_price=ol['unit_price']) + unit_price=unit_price) + product.inventory_amount -= ol['amount'] product.save() orderlines.append(u"{0} {1}".format(ol['amount'], product)) - - messages.success(request, u'{0} {1} kjøpte {2}.'.format( - order.customer.first_name, - order.customer.last_name, - u", ".join(orderlines) - )) + + if order.is_external(): + messages.success(request, u'Ekstern kjøpte {0}.'.format(u", ".join(orderlines))) + else: + messages.success(request, u'{0} {1} kjøpte {2}.'.format( + order.customer.first_name, + order.customer.last_name, + u", ".join(orderlines) + )) + return HttpResponseRedirect(reverse('main.views.register')) else: # TODO specify error(s) @@ -216,7 +235,10 @@ def stats_product(product): .reverse() f_per_user = [] for row in per_user: - who = row['order__customer__first_name'] + " " + row['order__customer__last_name'] + if row['order__customer__first_name'] is not None: + who = row['order__customer__first_name'] + " " + row['order__customer__last_name'] + else: + who = u"Ekstern" num = row['num'] f_per_user.append([who, num]) response = { @@ -249,14 +271,19 @@ def stats_products(request, product=None): def stats_orders(request): + num_external_orders = Order.objects.filter(customer=None).count() per_user = Order.objects\ .values('customer__first_name', 'customer__last_name')\ .annotate(num=Count('customer'))\ .order_by('num').reverse() f_per_user = [] for row in per_user: - who = row['customer__first_name'] + " " + row['customer__last_name'] - num = row['num'] + if row['customer__first_name'] is not None: + who = row['customer__first_name'] + " " + row['customer__last_name'] + num = row['num'] + else: + who = u"Ekstern" + num = num_external_orders f_per_user.append([who, num]) return HttpResponse(json.dumps(f_per_user), content_type='application/javascript; charset=utf8') @@ -338,7 +365,7 @@ def inventory(request): products = Product.objects.all().order_by('-active', 'inventory_amount', 'name') transactions = InventoryTransaction.objects.all().order_by('-created') products_active = products.filter(active=True) - inventory_value = sum(map(lambda x: x[0] * x[1], products_active.values_list('inventory_amount', 'sale_price_int'))) + wholesale_value = sum([p.wholesale_value for p in products_active]) num_products = sum(products_active.values_list('inventory_amount', flat=True)) return render(request, 'inventory.html', locals()) diff --git a/templates/inventory.html b/templates/inventory.html index d478c20..3c5f7be 100644 --- a/templates/inventory.html +++ b/templates/inventory.html @@ -20,8 +20,8 @@ Vare Beholdning - Pris (intern) - Salgsverdi (intern) + Innkjøpspris + Verdi I salg Sist oppdatert @@ -31,19 +31,18 @@ {{ product.name }} {{ product.inventory_amount }} - {{ product.sale_price_int }} - {{ product.inventory_value }} + {{ product.wholesale_unit_price|default:"-" }} + {{ product.wholesale_value|default:"-" }} {% if product.active %}{% endif %} {{ product.updated }} {% endfor %} - Sum - {{ num_products }} - {{ inventory_value }} + {{ num_products }} totalt + {{ wholesale_value }} diff --git a/templates/log.html b/templates/log.html index d76bfd7..69516ea 100644 --- a/templates/log.html +++ b/templates/log.html @@ -38,7 +38,7 @@ {% for order in orders %} {{ order.created }} - {% if order.customer.get_profile.image %}{% else %}{{ order.customer.first_name }} {{ order.customer.last_name|slice:":1"}}{% endif %} + {% if order.customer %}{% if order.customer.get_profile.image %}{% else %}{{ order.customer.first_name }} {{ order.customer.last_name|slice:":1"}}{% endif %}{% else %}Ekstern{% endif %} {% for line in order.orderline_set.all %} {{ line.amount}} {{ line.product }}{% if not forloop.last %}, {% endif %}{% endfor %} {{ order.order_sum }} diff --git a/templates/register.html b/templates/register.html index 8e6f54d..503dffd 100644 --- a/templates/register.html +++ b/templates/register.html @@ -9,7 +9,7 @@
{% for message in messages %} -
{{ message }}×
+ {{ message }}×
{% endfor %}
@@ -41,6 +41,11 @@