]> git.0d.be Git - django-panik-matos.git/commitdiff
basic system to loan pieces
authorFrédéric Péters <fpeters@0d.be>
Sun, 29 Jun 2014 19:11:13 +0000 (21:11 +0200)
committerFrédéric Péters <fpeters@0d.be>
Sun, 29 Jun 2014 19:11:13 +0000 (21:11 +0200)
matos/forms.py [new file with mode: 0644]
matos/migrations/0004_auto__add_loanpiece__add_loan.py [new file with mode: 0644]
matos/models.py
matos/templates/matos/loan_detail.html [new file with mode: 0644]
matos/templates/matos/loan_form.html [new file with mode: 0644]
matos/urls.py
matos/views.py

diff --git a/matos/forms.py b/matos/forms.py
new file mode 100644 (file)
index 0000000..5d067db
--- /dev/null
@@ -0,0 +1,9 @@
+from django import forms
+
+from .models import Loan
+
+
+class LoanForm(forms.ModelForm):
+    class Meta:
+        model = Loan
+        exclude = ('pieces')
diff --git a/matos/migrations/0004_auto__add_loanpiece__add_loan.py b/matos/migrations/0004_auto__add_loanpiece__add_loan.py
new file mode 100644 (file)
index 0000000..44fd726
--- /dev/null
@@ -0,0 +1,74 @@
+# -*- coding: utf-8 -*-
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+
+class Migration(SchemaMigration):
+
+    def forwards(self, orm):
+        # Adding model 'LoanPiece'
+        db.create_table(u'matos_loanpiece', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('piece', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['matos.Piece'])),
+            ('loan', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['matos.Loan'])),
+            ('quantity', self.gf('django.db.models.fields.IntegerField')(default=1)),
+        ))
+        db.send_create_signal(u'matos', ['LoanPiece'])
+
+        # Adding model 'Loan'
+        db.create_table(u'matos_loan', (
+            (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+            ('date_start', self.gf('django.db.models.fields.DateField')(null=True)),
+            ('date_end', self.gf('django.db.models.fields.DateField')(null=True)),
+            ('comment', self.gf('django.db.models.fields.TextField')(blank=True)),
+            ('loaner', self.gf('django.db.models.fields.CharField')(max_length=50, blank=True)),
+        ))
+        db.send_create_signal(u'matos', ['Loan'])
+
+
+    def backwards(self, orm):
+        # Deleting model 'LoanPiece'
+        db.delete_table(u'matos_loanpiece')
+
+        # Deleting model 'Loan'
+        db.delete_table(u'matos_loan')
+
+
+    models = {
+        u'matos.loan': {
+            'Meta': {'object_name': 'Loan'},
+            'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'date_end': ('django.db.models.fields.DateField', [], {'null': 'True'}),
+            'date_start': ('django.db.models.fields.DateField', [], {'null': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'loaner': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+            'pieces': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'to': u"orm['matos.Piece']", 'null': 'True', 'through': u"orm['matos.LoanPiece']", 'blank': 'True'})
+        },
+        u'matos.loanpiece': {
+            'Meta': {'object_name': 'LoanPiece'},
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'loan': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['matos.Loan']"}),
+            'piece': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['matos.Piece']"}),
+            'quantity': ('django.db.models.fields.IntegerField', [], {'default': '1'})
+        },
+        u'matos.piece': {
+            'Meta': {'ordering': "['title']", 'object_name': 'Piece'},
+            'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'creation_timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'null': 'True', 'blank': 'True'}),
+            u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'image': ('django.db.models.fields.files.ImageField', [], {'max_length': '250', 'null': 'True', 'blank': 'True'}),
+            'in_use': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'last_update_timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'null': 'True', 'blank': 'True'}),
+            'location': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'purchase_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'purchase_price': ('django.db.models.fields.DecimalField', [], {'null': 'True', 'max_digits': '10', 'decimal_places': '2', 'blank': 'True'}),
+            'quantity': ('django.db.models.fields.IntegerField', [], {'default': '1', 'null': 'True'}),
+            'reference': ('django.db.models.fields.CharField', [], {'max_length': '50', 'blank': 'True'}),
+            'rentable': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
+            'title': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        }
+    }
+
+    complete_apps = ['matos']
\ No newline at end of file
index aaaa5358332a2e86f2cb0be9bc453babf6a6fe99..48c253a9317a2ab10eb947ee795afa7592a4912b 100644 (file)
@@ -27,5 +27,32 @@ class Piece(models.Model):
     creation_timestamp = models.DateTimeField(auto_now_add=True, null=True)
     last_update_timestamp = models.DateTimeField(auto_now=True, null=True)
 
+    def __unicode__(self):
+        return self.title
+
     def get_absolute_url(self):
         return reverse('piece-view', kwargs={'pk': self.id})
+
+
+class Loan(models.Model):
+
+    class Meta:
+        verbose_name = _('Loan')
+        verbose_name_plural = _('Loans')
+
+    date_start = models.DateField(null=True)
+    date_end = models.DateField(null=True)
+    comment = models.TextField(blank=True)
+
+    loaner = models.CharField(_('Loaner'), blank=True, max_length=50)
+    pieces = models.ManyToManyField(Piece, verbose_name=_('Pieces'),
+            null=True, blank=True, through='LoanPiece')
+
+    def get_absolute_url(self):
+        return reverse('loan-view', kwargs={'pk': self.id})
+
+
+class LoanPiece(models.Model):
+    piece = models.ForeignKey(Piece)
+    loan = models.ForeignKey(Loan)
+    quantity = models.IntegerField(_('Quantity'), default=1)
diff --git a/matos/templates/matos/loan_detail.html b/matos/templates/matos/loan_detail.html
new file mode 100644 (file)
index 0000000..1b47b0b
--- /dev/null
@@ -0,0 +1,28 @@
+{% extends "matos/base.html" %}
+{% load i18n %}
+
+{% block appbar %}
+<h2>{{ object.date_start }} → {{ object.date_end }}</h2>
+<span><a href="{% url 'pieces-list' %}">{% trans 'Back to inventory' %}</a></span>
+{% endblock %}
+
+{% block content %}
+
+<p>
+{% if perms.matos.edit_loan %}
+<a class="big-friendly-button" href="{% url 'loan-update' pk=loan.id %}">{% trans 'Edit Details' %}</a>
+{% endif %}
+{% if perms.matos.delete_loan %}
+<a class="big-friendly-button actually-not-that-friendly"
+        href="{% url 'loan-delete' pk=loan.id %}">{% trans 'Delete' %}</a>
+{% endif %}
+</p>
+
+{% if object.comment %}
+<h3>{% trans 'Comment' %}</h3>
+<p>
+{{ object.comment }}
+</p>
+{% endif %}
+
+{% endblock %}
diff --git a/matos/templates/matos/loan_form.html b/matos/templates/matos/loan_form.html
new file mode 100644 (file)
index 0000000..a25f417
--- /dev/null
@@ -0,0 +1,64 @@
+{% extends "matos/base.html" %}
+{% load i18n %}
+
+{% block appbar %}
+{% if object %}
+<h2>{{ object.date_start }} → {{ object.date_end }}</h2>
+<span><a href="{% url 'loan-view' pk=object.id %}">{% trans 'Back to Loan' %}</a></span>
+{% else %}
+<h2>{% trans 'New Loan' %}</h2>
+<span><a href="{% url 'pieces-list' %}">{% trans 'Back to inventory' %}</a></span>
+{% endif %}
+{% endblock %}
+
+{% block content %}
+    <form method="post" enctype="multipart/form-data">
+      <div id="form-content" class="matos-loan">
+        {% csrf_token %}
+        <div class="loan-infos">
+        {{ form.as_p }}
+        </div>
+        <div class="loan-pieces">
+          <label>{% trans 'Pieces' %}</label>
+          <ul>
+            {% for piece in pieces %}
+            <li data-piece-id="{{ piece.id }}"
+                data-piece-quantity="{{ piece.quantity }}">
+                <button class="plus">+</button>
+                <button class="minus">-</button>
+                <span>{{ piece.title }}</span></li>
+            {% endfor %}
+          </ul>
+          <select style="display: none;" name="id_pieces" id="id_pieces" multiple>
+                  {% for loan in loaned %}
+                  <option selected value="{{ loan }}">{{ loan }}</option>
+                  {% endfor %}
+          </select>
+        </div>
+      {% block buttons %}
+      <div class="buttons">
+      <button>{% trans 'Save' %}</button>
+      </div>
+      {% endblock %}
+      </div>
+    </form>
+
+{% endblock %}
+
+{% block page-end %}
+<script>
+$(document).ready(function() {
+  $('.loan-pieces button.plus').click(function() {
+    var piece_id = $(this).parent().data('piece-id');
+    var piece_label = $(this).next().next('span').text();
+    $('#id_pieces').append('<option selected value=' + piece_id + '>' + piece_label + '</option>')
+    return false;
+  });
+  $('.loan-pieces button.minus').click(function() {
+    var piece_id = $(this).parent().data('piece-id');
+    $('#id_pieces option[value=' + piece_id + ']:first').remove();
+    return false;
+  });
+});
+</script>
+{% endblock %}
index 325288413a1146ee8ed227eafe29a11b432f4a23..198d6cfc0257ccfa8e8fd9bd8d5927a94b4ab44b 100644 (file)
@@ -8,4 +8,9 @@ urlpatterns = patterns('',
     url(r'^piece/(?P<pk>\d+)/$', PieceDetailView.as_view(), name='piece-view'),
     url(r'^piece/(?P<pk>\d+)/update', PieceUpdateView.as_view(), name='piece-update'),
     url(r'^piece/(?P<pk>\d+)/delete$', PieceDeleteView.as_view(), name='piece-delete'),
+
+    url(r'^loan/new$', LoanCreateView.as_view(), name='loan-create'),
+    url(r'^loan/(?P<pk>\d+)/$', LoanDetailView.as_view(), name='loan-view'),
+    url(r'^loan/(?P<pk>\d+)/update$', LoanUpdateView.as_view(), name='loan-update'),
+    url(r'^loan/(?P<pk>\d+)/delete$', LoanDeleteView.as_view(), name='loan-delete'),
 )
index 7275b4c1656e0ce4610ae5c7ec49287e4080dd9f..e835788236ad1cbfeb86b1d4258319b783cae9d4 100644 (file)
@@ -4,7 +4,8 @@ from django.views.generic.list import ListView
 from django.views.generic.detail import DetailView
 from django.views.generic.edit import CreateView, UpdateView, DeleteView
 
-from .models import Piece
+from .models import Piece, Loan, LoanPiece
+from .forms import LoanForm
 
 
 class PieceListView(ListView):
@@ -28,3 +29,50 @@ class PieceDeleteView(DeleteView):
 
     def get_success_url(self):
         return reverse('pieces-list')
+
+
+class LoanCreateView(CreateView):
+    model = Loan
+    form_class = LoanForm
+
+    def get_context_data(self, *args, **kwargs):
+        context = super(LoanCreateView, self).get_context_data(*args, **kwargs)
+        context['pieces'] = Piece.objects.filter(rentable=True)
+        return context
+
+
+class LoanDetailView(DetailView):
+    model = Loan
+
+
+class LoanUpdateView(UpdateView):
+    model = Loan
+    form_class = LoanForm
+
+    def form_valid(self, form):
+        pieces = {}
+        for piece in self.request.POST.getlist('id_pieces'):
+            if piece not in pieces:
+                pieces[piece] = 0
+            pieces[piece] += 1
+        result = super(LoanUpdateView, self).form_valid(form)
+        self.object.pieces.clear()
+        for piece_id, quantity in pieces.items():
+            LoanPiece.objects.create(piece=Piece.objects.get(id=piece_id),
+                    loan=self.object, quantity=quantity)
+        return result
+
+    def get_context_data(self, *args, **kwargs):
+        context = super(LoanUpdateView, self).get_context_data(*args, **kwargs)
+        context['pieces'] = Piece.objects.filter(rentable=True)
+        context['loaned'] = []
+        for x in LoanPiece.objects.filter(loan=self.object):
+            context['loaned'].extend([x.id]*x.quantity)
+        return context
+
+
+class LoanDeleteView(DeleteView):
+    model = Loan
+
+    def get_success_url(self):
+        return reverse('pieces-list')