--- /dev/null
+from django import forms
+
+from .models import Loan
+
+
+class LoanForm(forms.ModelForm):
+ class Meta:
+ model = Loan
+ exclude = ('pieces')
--- /dev/null
+# -*- 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
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)
--- /dev/null
+{% 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 %}
--- /dev/null
+{% 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 %}
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'),
)
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):
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')