API changes

- Allow SupplierPart to be filtered by 'company' in addition to 'supplier' and 'manufacturer'
- Stock can now also be filtered by 'company'
This commit is contained in:
Oliver Walters 2020-04-13 18:50:59 +10:00
parent 635c4339e0
commit 1b1cd944be
10 changed files with 81 additions and 43 deletions

View File

@ -69,14 +69,6 @@ function loadCompanyTable(table, url, options={}) {
return '';
}
},
{
field: 'part_count',
title: 'Parts',
sortable: true,
formatter: function(value, row, index, field) {
return renderLink(value, row.url + 'parts/');
}
},
],
});
}

View File

@ -81,12 +81,19 @@ class SupplierPartList(generics.ListCreateAPIView):
queryset = SupplierPart.objects.all().prefetch_related(
'part',
'part__category',
'part__stock_items',
'part__bom_items',
'part__builds',
'supplier',
'pricebreaks')
'manufacturer'
)
def get_queryset(self):
queryset = super().get_queryset()
# Filter by EITHER manufacturer or supplier
company = self.request.query_params.get('company', None)
if company is not None:
queryset = queryset.filter(Q(manufacturer=company) | Q(supplier=company))
def get_serializer(self, *args, **kwargs):

View File

@ -13,7 +13,7 @@ from decimal import Decimal
from django.utils.translation import gettext_lazy as _
from django.core.validators import MinValueValidator
from django.db import models
from django.db.models import Sum
from django.db.models import Sum, Q
from django.apps import apps
from django.urls import reverse
@ -140,26 +140,44 @@ class Company(models.Model):
return getBlankThumbnail()
@property
def part_count(self):
def manufactured_part_count(self):
""" The number of parts manufactured by this company """
return self.manufactured_parts.count()
@property
def has_manufactured_parts(self):
return self.manufactured_part_count > 0
@property
def supplied_part_count(self):
""" The number of parts supplied by this company """
return self.supplied_parts.count()
@property
def has_supplied_parts(self):
""" Return True if this company supplies any parts """
return self.supplied_part_count > 0
@property
def parts(self):
""" Return SupplierPart objects which are supplied or manufactured by this company """
return SupplierPart.objects.filter(Q(supplier=self.id) | Q(manufacturer=self.id))
@property
def part_count(self):
""" The number of parts manufactured (or supplied) by this Company """
return self.parts.count()
@property
def has_parts(self):
""" Return True if this company supplies any parts """
return self.part_count > 0
@property
def stock_items(self):
""" Return a list of all stock items supplied by this company """
""" Return a list of all stock items supplied or manufactured by this company """
stock = apps.get_model('stock', 'StockItem')
return stock.objects.filter(supplier_part__supplier=self.id).all()
return stock.objects.filter(Q(supplier_part__supplier=self.id) | Q(supplier_part__manufacturer=self.id)).all()
@property
def stock_count(self):
""" Return the number of stock items supplied by this company """
stock = apps.get_model('stock', 'StockItem')
return stock.objects.filter(supplier_part__supplier=self.id).count()
""" Return the number of stock items supplied or manufactured by this company """
return self.stock_items.count()
def outstanding_purchase_orders(self):
""" Return purchase orders which are 'outstanding' """
@ -255,7 +273,7 @@ class SupplierPart(models.Model):
)
supplier = models.ForeignKey(Company, on_delete=models.CASCADE,
related_name='parts',
related_name='supplied_parts',
limit_choices_to={'is_supplier': True},
help_text=_('Select supplier'),
)

View File

@ -6,8 +6,8 @@ Are you sure you want to delete company '{{ company.name }}'?
<br>
{% if company.part_count > 0 %}
<p>There are {{ company.part_count }} parts sourced from this company.<br>
{% if company.supplied_part_count > 0 %}
<p>There are {{ company.supplied_part_count }} parts sourced from this company.<br>
If this supplier is deleted, these supplier part entries will also be deleted.</p>
<ul class='list-group'>
{% for part in company.parts.all %}

View File

@ -12,15 +12,20 @@
<col width='25'>
<col>
<tr>
<td><span class='fas fa-user-tag'></span></td>
<td>{% trans "Customer" %}</td>
<td>{% include 'yesnolabel.html' with value=company.is_customer %}</td>
<td><span class='fas fa-industry'></span></td>
<td>{% trans "Manufacturer" %}</td>
<td>{% include "yesnolabel.html" with value=company.is_manufacturer %}</td>
</tr>
<tr>
<td><span class='fas fa-industry'></span></td>
<td><span class='fas fa-building'></span></td>
<td>{% trans "Supplier" %}</td>
<td>{% include 'yesnolabel.html' with value=company.is_supplier %}</td>
</tr>
<tr>
<td><span class='fas fa-user-tie'></span></td>
<td>{% trans "Customer" %}</td>
<td>{% include 'yesnolabel.html' with value=company.is_customer %}</td>
</tr>
</table>
{% endblock %}

View File

@ -19,8 +19,9 @@
loadStockTable($('#stock-table'), {
url: "{% url 'api-stock-list' %}",
params: {
supplier: {{ company.id }},
company: {{ company.id }},
part_detail: true,
supplier_detail: true,
location_detail: true,
},
buttons: [

View File

@ -4,13 +4,15 @@
<li{% if tab == 'details' %} class='active'{% endif %}>
<a href="{% url 'company-detail' company.id %}">{% trans "Details" %}</a>
</li>
{% if company.is_supplier %}
{% if company.is_supplier or company.is_manufacturer %}
<li{% if tab == 'parts' %} class='active'{% endif %}>
<a href="{% url 'company-detail-parts' company.id %}">{% trans "Supplier Parts" %} <span class='badge'>{{ company.part_count }}</span></a>
<a href="{% url 'company-detail-parts' company.id %}">{% trans "Parts" %} <span class='badge'>{{ company.part_count }}</span></a>
</li>
<li{% if tab == 'stock' %} class='active'{% endif %}>
<a href="{% url 'company-detail-stock' company.id %}">{% trans "Stock" %} <span class='badge'>{{ company.stock_count }}</a>
</li>
{% endif %}
{% if company.is_supplier %}
<li{% if tab == 'po' %} class='active'{% endif %}>
<a href="{% url 'company-detail-purchase-orders' company.id %}">{% trans "Purchase Orders" %} <span class='badge'>{{ company.purchase_orders.count }}</span></a>
</li>

View File

@ -56,13 +56,13 @@ class CompanySimpleTest(TestCase):
zerg = Company.objects.get(pk=3)
self.assertTrue(acme.has_parts)
self.assertEqual(acme.part_count, 4)
self.assertEqual(acme.supplied_part_count, 4)
self.assertTrue(appel.has_parts)
self.assertEqual(appel.part_count, 2)
self.assertEqual(appel.supplied_part_count, 2)
self.assertTrue(zerg.has_parts)
self.assertEqual(zerg.part_count, 1)
self.assertEqual(zerg.supplied_part_count, 1)
def test_price_breaks(self):

View File

@ -15,7 +15,7 @@ company_detail_urls = [
# url(r'orders/?', views.CompanyDetail.as_view(template_name='company/orders.html'), name='company-detail-orders'),
url(r'parts/?', views.CompanyDetail.as_view(template_name='company/detail_part.html'), name='company-detail-parts'),
url(r'parts/', views.CompanyDetail.as_view(template_name='company/detail_part.html'), name='company-detail-parts'),
url(r'stock/?', views.CompanyDetail.as_view(template_name='company/detail_stock.html'), name='company-detail-stock'),
url(r'purchase-orders/?', views.CompanyDetail.as_view(template_name='company/detail_purchase_orders.html'), name='company-detail-purchase-orders'),
url(r'notes/?', views.CompanyNotes.as_view(), name='company-notes'),

View File

@ -8,6 +8,7 @@ from django_filters import NumberFilter
from django.conf import settings
from django.conf.urls import url, include
from django.urls import reverse
from django.db.models import Q
from .models import StockLocation, StockItem
from .models import StockItemTracking
@ -494,11 +495,23 @@ class StockList(generics.ListCreateAPIView):
if supplier_part_id:
stock_list = stock_list.filter(supplier_part=supplier_part_id)
# Filter by supplier ID
supplier_id = self.request.query_params.get('supplier', None)
# Filter by company (either manufacturer or supplier)
company = self.request.query_params.get('company', None)
if supplier_id:
stock_list = stock_list.filter(supplier_part__supplier=supplier_id)
if company is not None:
stock_list = stock_list.filter(Q(supplier_part__supplier=company) | Q(supplier_part__manufacturer=company))
# Filter by supplier
supplier = self.request.query_params.get('supplier', None)
if supplier is not None:
stock_list = stock_list.filter(supplier_part__supplier=supplier)
# Filter by manufacturer
manufacturer = self.request.query_params.get('manufacturer', None)
if manufacturer is not None:
stock_list = stock_list.filter(supplier_part__manufacturer=manufacturer)
# Also ensure that we pre-fecth all the related items
stock_list = stock_list.prefetch_related(