• Есть идеи, как реализовать создание/хранение приходной/расходной накладной на Django?

    neatsoft
    @neatsoft
    Life is too short for bad software
    В одной таблице должны храниться документы, во второй - строки табличной части (всех документов).

    models.py:
    from django.db import models
    from django.db.models import Max, Sum
    from django.utils.translation import ugettext_lazy as _
    
    
    class Product(models.Model):
        name = models.CharField(
            _('Name'),
            max_length=200,
            db_index=True,
        )
    
        def __str__(self):
            return self.name
    
    
    class Document(models.Model):
        INVOICE = 'I'
        WAYBILL = 'W'
        TYPE_CHOICES = (
            (INVOICE, _('Invoice')),
            (WAYBILL, _('Waybill')),
        )
        type = models.CharField(
            _('Type'),
            max_length=1,
            choices=TYPE_CHOICES,
        )
        number = models.CharField(
            _('Number'),
            blank=True,
            max_length=50,
        )
        created_at = models.DateTimeField(
            _('Created'),
            auto_now_add=True,
            db_index=True,
        )
    
        @property
        def total(self):
            return self.items.aggregate(sum=Sum('total'))['sum']
    
        class Meta:
            ordering = ['-created_at']
    
    
    class DocumentItem(models.Model):
        document = models.ForeignKey(
            Document,
            models.CASCADE,
            related_name='items',
        )
        position = models.PositiveIntegerField(
            verbose_name=_('Position'),
            editable=False,
            db_index=True,
        )
        product = models.ForeignKey(
            Product,
            models.PROTECT,
        )
        price = models.DecimalField(
            _('Price'),
            max_digits=12,
            decimal_places=2,
        )
        quantity = models.DecimalField(
            _('Quantity'),
            max_digits=10,
            decimal_places=3,
        )
        total = models.DecimalField(
            _('Total'),
            max_digits=12,
            decimal_places=2,
        )
    
        def save(self, *args, **kwargs):
            if not self.position:
                position = self.document.items.aggregate(Max('position'))['position__max'] or 0
                self.position = position + 1
            super(DocumentItem, self).save(*args, **kwargs)


    admin.py:
    from django.contrib import admin
    
    from .models import Product, Document, DocumentItem
    
    
    @admin.register(Product)
    class ProductAdmin(admin.ModelAdmin):
        pass
    
    
    class DocumentItemInline(admin.TabularInline):
        model = DocumentItem
        fields = (
            'position',
            'product',
            'price',
            'quantity',
            'total',
        )
        readonly_fields = (
            'position',
        )
        ordering = ['position']
    
    
    @admin.register(Document)
    class DocumentAdmin(admin.ModelAdmin):
        inlines = [
            DocumentItemInline,
        ]
        list_display = (
            'type',
            'number',
            'created_at',
            'total',
        )
        list_filter = (
            'type',
        )
        search_fields = (
            '=number',
        )


    Стандартная админка - для примера, вообще она не для этого (обычным пользователям она не должна быть доступна). Но реальные формы строятся по тому же принципу.

    Код накидал прям здесь, не проверял, поэтому возможны незначительные ошибки.
    Ответ написан
    4 комментария