Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver 2021-06-21 00:51:43 +10:00
commit 09e7d6898b
13 changed files with 465 additions and 106 deletions

View File

@ -0,0 +1,60 @@
"""
Custom management command to rebuild all MPTT models
- This is crucial after importing any fixtures, etc
"""
from django.core.management.base import BaseCommand
class Command(BaseCommand):
"""
Rebuild all database models which leverage the MPTT structure.
"""
def handle(self, *args, **kwargs):
# Part model
try:
print("Rebuilding Part objects")
from part.models import Part
Part.objects.rebuild()
except:
print("Error rebuilding Part objects")
# Part category
try:
print("Rebuilding PartCategory objects")
from part.models import PartCategory
PartCategory.objects.rebuild()
except:
print("Error rebuilding PartCategory objects")
# StockItem model
try:
print("Rebuilding StockItem objects")
from stock.models import StockItem
StockItem.objects.rebuild()
except:
print("Error rebuilding StockItem objects")
# StockLocation model
try:
print("Rebuilding StockLocation objects")
from stock.models import StockLocation
StockLocation.objects.rebuild()
except:
print("Error rebuilding StockLocation objects")
# Build model
try:
print("Rebuilding Build objects")
from build.models import Build
Build.objects.rebuild()
except:
print("Error rebuilding Build objects")

View File

@ -165,6 +165,19 @@ class BuildItemList(generics.ListCreateAPIView):
serializer_class = BuildItemSerializer serializer_class = BuildItemSerializer
def get_serializer(self, *args, **kwargs):
try:
params = self.request.query_params
kwargs['part_detail'] = str2bool(params.get('part_detail', False))
kwargs['build_detail'] = str2bool(params.get('build_detail', False))
kwargs['location_detail'] = str2bool(params.get('location_detail', False))
except AttributeError:
pass
return self.serializer_class(*args, **kwargs)
def get_queryset(self): def get_queryset(self):
""" Override the queryset method, """ Override the queryset method,
to allow filtering by stock_item.part to allow filtering by stock_item.part

View File

@ -13,7 +13,8 @@ from rest_framework import serializers
from InvenTree.serializers import InvenTreeModelSerializer from InvenTree.serializers import InvenTreeModelSerializer
from stock.serializers import StockItemSerializerBrief from stock.serializers import StockItemSerializerBrief
from part.serializers import PartBriefSerializer from stock.serializers import LocationSerializer
from part.serializers import PartSerializer, PartBriefSerializer
from .models import Build, BuildItem from .models import Build, BuildItem
@ -99,22 +100,45 @@ class BuildItemSerializer(InvenTreeModelSerializer):
bom_part = serializers.IntegerField(source='bom_item.sub_part.pk', read_only=True) bom_part = serializers.IntegerField(source='bom_item.sub_part.pk', read_only=True)
part = serializers.IntegerField(source='stock_item.part.pk', read_only=True) part = serializers.IntegerField(source='stock_item.part.pk', read_only=True)
part_name = serializers.CharField(source='stock_item.part.full_name', read_only=True) location = serializers.IntegerField(source='stock_item.location.pk', read_only=True)
part_thumb = serializers.CharField(source='getStockItemThumbnail', read_only=True)
# Extra (optional) detail fields
part_detail = PartSerializer(source='stock_item.part', many=False, read_only=True)
build_detail = BuildSerializer(source='build', many=False, read_only=True)
stock_item_detail = StockItemSerializerBrief(source='stock_item', read_only=True) stock_item_detail = StockItemSerializerBrief(source='stock_item', read_only=True)
location_detail = LocationSerializer(source='stock_item.location', read_only=True)
quantity = serializers.FloatField() quantity = serializers.FloatField()
def __init__(self, *args, **kwargs):
build_detail = kwargs.pop('build_detail', False)
part_detail = kwargs.pop('part_detail', False)
location_detail = kwargs.pop('location_detail', False)
super().__init__(*args, **kwargs)
if not build_detail:
self.fields.pop('build_detail')
if not part_detail:
self.fields.pop('part_detail')
if not location_detail:
self.fields.pop('location_detail')
class Meta: class Meta:
model = BuildItem model = BuildItem
fields = [ fields = [
'pk', 'pk',
'bom_part', 'bom_part',
'build', 'build',
'build_detail',
'install_into', 'install_into',
'location',
'location_detail',
'part', 'part',
'part_name', 'part_detail',
'part_thumb',
'stock_item', 'stock_item',
'stock_item_detail', 'stock_item_detail',
'quantity' 'quantity'

View File

@ -103,17 +103,11 @@ class ManufacturerPartList(generics.ListCreateAPIView):
# Do we wish to include extra detail? # Do we wish to include extra detail?
try: try:
kwargs['part_detail'] = str2bool(self.request.query_params.get('part_detail', None)) params = self.request.query_params
except AttributeError:
pass
try: kwargs['part_detail'] = str2bool(params.get('part_detail', None))
kwargs['manufacturer_detail'] = str2bool(self.request.query_params.get('manufacturer_detail', None)) kwargs['manufacturer_detail'] = str2bool(params.get('manufacturer_detail', None))
except AttributeError: kwargs['pretty'] = str2bool(params.get('pretty', None))
pass
try:
kwargs['pretty'] = str2bool(self.request.query_params.get('pretty', None))
except AttributeError: except AttributeError:
pass pass
@ -252,22 +246,11 @@ class SupplierPartList(generics.ListCreateAPIView):
# Do we wish to include extra detail? # Do we wish to include extra detail?
try: try:
kwargs['part_detail'] = str2bool(self.request.query_params.get('part_detail', None)) params = self.request.query_params
except AttributeError: kwargs['part_detail'] = str2bool(params.get('part_detail', None))
pass kwargs['supplier_detail'] = str2bool(params.get('supplier_detail', None))
kwargs['manufacturer_detail'] = str2bool(self.params.get('manufacturer_detail', None))
try: kwargs['pretty'] = str2bool(params.get('pretty', None))
kwargs['supplier_detail'] = str2bool(self.request.query_params.get('supplier_detail', None))
except AttributeError:
pass
try:
kwargs['manufacturer_detail'] = str2bool(self.request.query_params.get('manufacturer_detail', None))
except AttributeError:
pass
try:
kwargs['pretty'] = str2bool(self.request.query_params.get('pretty', None))
except AttributeError: except AttributeError:
pass pass

View File

@ -22,9 +22,10 @@ from .models import PurchaseOrder, PurchaseOrderLineItem
from .models import PurchaseOrderAttachment from .models import PurchaseOrderAttachment
from .serializers import POSerializer, POLineItemSerializer, POAttachmentSerializer from .serializers import POSerializer, POLineItemSerializer, POAttachmentSerializer
from .models import SalesOrder, SalesOrderLineItem from .models import SalesOrder, SalesOrderLineItem, SalesOrderAllocation
from .models import SalesOrderAttachment from .models import SalesOrderAttachment
from .serializers import SalesOrderSerializer, SOLineItemSerializer, SOAttachmentSerializer from .serializers import SalesOrderSerializer, SOLineItemSerializer, SOAttachmentSerializer
from .serializers import SalesOrderAllocationSerializer
class POList(generics.ListCreateAPIView): class POList(generics.ListCreateAPIView):
@ -422,17 +423,11 @@ class SOLineItemList(generics.ListCreateAPIView):
def get_serializer(self, *args, **kwargs): def get_serializer(self, *args, **kwargs):
try: try:
kwargs['part_detail'] = str2bool(self.request.query_params.get('part_detail', False)) params = self.request.query_params
except AttributeError:
pass
try: kwargs['part_detail'] = str2bool(params.get('part_detail', False))
kwargs['order_detail'] = str2bool(self.request.query_params.get('order_detail', False)) kwargs['order_detail'] = str2bool(params.get('order_detail', False))
except AttributeError: kwargs['allocations'] = str2bool(params.get('allocations', False))
pass
try:
kwargs['allocations'] = str2bool(self.request.query_params.get('allocations', False))
except AttributeError: except AttributeError:
pass pass
@ -486,6 +481,70 @@ class SOLineItemDetail(generics.RetrieveUpdateAPIView):
serializer_class = SOLineItemSerializer serializer_class = SOLineItemSerializer
class SOAllocationList(generics.ListCreateAPIView):
"""
API endpoint for listing SalesOrderAllocation objects
"""
queryset = SalesOrderAllocation.objects.all()
serializer_class = SalesOrderAllocationSerializer
def get_serializer(self, *args, **kwargs):
try:
params = self.request.query_params
kwargs['part_detail'] = str2bool(params.get('part_detail', False))
kwargs['item_detail'] = str2bool(params.get('item_detail', False))
kwargs['order_detail'] = str2bool(params.get('order_detail', False))
kwargs['location_detail'] = str2bool(params.get('location_detail', False))
except AttributeError:
pass
return self.serializer_class(*args, **kwargs)
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
# Filter by order
params = self.request.query_params
# Filter by "part" reference
part = params.get('part', None)
if part is not None:
queryset = queryset.filter(item__part=part)
# Filter by "order" reference
order = params.get('order', None)
if order is not None:
queryset = queryset.filter(line__order=order)
# Filter by "outstanding" order status
outstanding = params.get('outstanding', None)
if outstanding is not None:
outstanding = str2bool(outstanding)
if outstanding:
queryset = queryset.filter(line__order__status__in=SalesOrderStatus.OPEN)
else:
queryset = queryset.exclude(line__order__status__in=SalesOrderStatus.OPEN)
return queryset
filter_backends = [
DjangoFilterBackend,
]
# Default filterable fields
filter_fields = [
'item',
]
class POAttachmentList(generics.ListCreateAPIView, AttachmentMixin): class POAttachmentList(generics.ListCreateAPIView, AttachmentMixin):
""" """
API endpoint for listing (and creating) a PurchaseOrderAttachment (file upload) API endpoint for listing (and creating) a PurchaseOrderAttachment (file upload)
@ -494,10 +553,6 @@ class POAttachmentList(generics.ListCreateAPIView, AttachmentMixin):
queryset = PurchaseOrderAttachment.objects.all() queryset = PurchaseOrderAttachment.objects.all()
serializer_class = POAttachmentSerializer serializer_class = POAttachmentSerializer
filter_fields = [
'order',
]
order_api_urls = [ order_api_urls = [
# API endpoints for purchase orders # API endpoints for purchase orders
@ -512,14 +567,26 @@ order_api_urls = [
url(r'^po-line/$', POLineItemList.as_view(), name='api-po-line-list'), url(r'^po-line/$', POLineItemList.as_view(), name='api-po-line-list'),
# API endpoints for sales ordesr # API endpoints for sales ordesr
url(r'^so/(?P<pk>\d+)/$', SODetail.as_view(), name='api-so-detail'), url(r'^so/', include([
url(r'so/attachment/', include([ url(r'^(?P<pk>\d+)/$', SODetail.as_view(), name='api-so-detail'),
url(r'^.*$', SOAttachmentList.as_view(), name='api-so-attachment-list'), url(r'attachment/', include([
url(r'^.*$', SOAttachmentList.as_view(), name='api-so-attachment-list'),
])),
# List all sales orders
url(r'^.*$', SOList.as_view(), name='api-so-list'),
])), ])),
url(r'^so/.*$', SOList.as_view(), name='api-so-list'),
# API endpoints for sales order line items # API endpoints for sales order line items
url(r'^so-line/(?P<pk>\d+)/$', SOLineItemDetail.as_view(), name='api-so-line-detail'), url(r'^so-line/', include([
url(r'^so-line/$', SOLineItemList.as_view(), name='api-so-line-list'), url(r'^(?P<pk>\d+)/$', SOLineItemDetail.as_view(), name='api-so-line-detail'),
url(r'^$', SOLineItemList.as_view(), name='api-so-line-list'),
])),
# API endpoints for sales order allocations
url(r'^so-allocation', include([
# List all sales order allocations
url(r'^.*$', SOAllocationList.as_view(), name='api-so-allocation-list'),
])),
] ]

View File

@ -18,6 +18,7 @@ from InvenTree.serializers import InvenTreeAttachmentSerializerField
from company.serializers import CompanyBriefSerializer, SupplierPartSerializer from company.serializers import CompanyBriefSerializer, SupplierPartSerializer
from part.serializers import PartBriefSerializer from part.serializers import PartBriefSerializer
from stock.serializers import LocationBriefSerializer from stock.serializers import LocationBriefSerializer
from stock.serializers import StockItemSerializer, LocationSerializer
from .models import PurchaseOrder, PurchaseOrderLineItem from .models import PurchaseOrder, PurchaseOrderLineItem
from .models import PurchaseOrderAttachment, SalesOrderAttachment from .models import PurchaseOrderAttachment, SalesOrderAttachment
@ -42,7 +43,7 @@ class POSerializer(InvenTreeModelSerializer):
""" """
Add extra information to the queryset Add extra information to the queryset
- Number of liens in the PurchaseOrder - Number of lines in the PurchaseOrder
- Overdue status of the PurchaseOrder - Overdue status of the PurchaseOrder
""" """
@ -236,11 +237,38 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
This includes some fields from the related model objects. This includes some fields from the related model objects.
""" """
location_path = serializers.CharField(source='get_location_path') part = serializers.PrimaryKeyRelatedField(source='item.part', read_only=True)
location_id = serializers.IntegerField(source='get_location') order = serializers.PrimaryKeyRelatedField(source='line.order', many=False, read_only=True)
serial = serializers.CharField(source='get_serial') serial = serializers.CharField(source='get_serial', read_only=True)
po = serializers.CharField(source='get_po') quantity = serializers.FloatField(read_only=True)
quantity = serializers.FloatField() location = serializers.PrimaryKeyRelatedField(source='item.location', many=False, read_only=True)
# Extra detail fields
order_detail = SalesOrderSerializer(source='line.order', many=False, read_only=True)
part_detail = PartBriefSerializer(source='item.part', many=False, read_only=True)
item_detail = StockItemSerializer(source='item', many=False, read_only=True)
location_detail = LocationSerializer(source='item.location', many=False, read_only=True)
def __init__(self, *args, **kwargs):
order_detail = kwargs.pop('order_detail', False)
part_detail = kwargs.pop('part_detail', False)
item_detail = kwargs.pop('item_detail', False)
location_detail = kwargs.pop('location_detail', False)
super().__init__(*args, **kwargs)
if not order_detail:
self.fields.pop('order_detail')
if not part_detail:
self.fields.pop('part_detail')
if not item_detail:
self.fields.pop('item_detail')
if not location_detail:
self.fields.pop('location_detail')
class Meta: class Meta:
model = SalesOrderAllocation model = SalesOrderAllocation
@ -250,10 +278,14 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
'line', 'line',
'serial', 'serial',
'quantity', 'quantity',
'location_id', 'location',
'location_path', 'location_detail',
'po',
'item', 'item',
'item_detail',
'order',
'order_detail',
'part',
'part_detail',
] ]

View File

@ -81,10 +81,10 @@ function showAllocationSubTable(index, row, element) {
}, },
}, },
{ {
field: 'location_id', field: 'location',
title: 'Location', title: 'Location',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
return renderLink(row.location_path, `/stock/location/${row.location_id}/`); return renderLink(row.location_path, `/stock/location/${row.location}/`);
}, },
}, },
{ {

View File

@ -8,52 +8,43 @@
{% endblock %} {% endblock %}
{% block heading %} {% block heading %}
{% trans "Part Stock Allocations" %} {% trans "Build Order Allocations" %}
{% endblock %} {% endblock %}
{% block details %} {% block details %}
<table class='table table-striped table-condensed' id='build-table'>
<tr> <table class='table table-striped table-condensed' id='build-order-table'></table>
<th>{% trans "Order" %}</th>
<th>{% trans "Stock Item" %}</th>
<th>{% trans "Quantity" %}</th>
</tr>
{% for allocation in part.build_order_allocations %}
<tr>
<td><a href="{% url 'build-detail' allocation.build.id %}">{% trans "Build Order" %}: {{ allocation.build }}</a></td>
<td><a href="{% url 'stock-item-detail' allocation.stock_item.id %}">{% trans "Stock Item" %}: {{ allocation.stock_item }}</a></td>
<td>{% decimal allocation.quantity %}</td>
</tr>
{% endfor %}
{% for allocation in part.sales_order_allocations %}
<tr>
<td><a href="{% url 'so-detail' allocation.line.order.id %}">{% trans "Sales Order" %}: {{ allocation.line.order }}</a></td>
<td><a href="{% url 'stock-item-detail' allocation.item.id %}">{% trans "Stock Item" %}: {{ allocation.item }}</a></td>
<td>{% decimal allocation.quantity %}</td>
</tr>
{% endfor %}
</table>
{% endblock %} {% endblock %}
{% block pre_content_panel %}
<div class='panel panel-default panel-inventree'>
<div class='panel-heading'>
<h4>{% trans "Sales Order Allocations" %}</h4>
</div>
<div class='panel-content'>
<table class='table table-striped table-condensed' id='sales-order-table'></table>
</div>
</div>
{% endblock %}
{% block js_ready %} {% block js_ready %}
{{ block.super }} {{ block.super }}
$("#build-table").inventreeTable({ loadSalesOrderAllocationTable("#sales-order-table", {
columns: [ params: {
{ part: {{ part.id }},
title: '{% trans "Order" %}', }
sortable: true, });
},
{ loadBuildOrderAllocationTable("#build-order-table", {
title: '{% trans "Stock Item" %}', params: {
sortable: true, part: {{ part.id }},
}, }
{
title: '{% trans "Quantity" %}',
sortable: true,
}
]
}); });
{% endblock %} {% endblock %}

View File

@ -195,8 +195,13 @@
</div> </div>
{% block pre_content_panel %}
{% endblock %}
<div class='panel panel-default panel-inventree'> <div class='panel panel-default panel-inventree'>
<div class='panel-heading'> <div class='panel-heading'>
<h4> <h4>
{% block heading %} {% block heading %}
@ -210,7 +215,11 @@
<!-- Specific part details go here... --> <!-- Specific part details go here... -->
{% endblock %} {% endblock %}
</div> </div>
</div> </div>
{% block post_content_panel %}
{% endblock %}
{% endblock %} {% endblock %}

View File

@ -155,6 +155,88 @@ function makeBuildOutputActionButtons(output, buildInfo, lines) {
} }
function loadBuildOrderAllocationTable(table, options={}) {
/**
* Load a table showing all the BuildOrder allocations for a given part
*/
options.params['part_detail'] = true;
options.params['build_detail'] = true;
options.params['location_detail'] = true;
var filters = loadTableFilters("buildorderallocation");
for (var key in options.params) {
filters[key] = options.params[key];
}
setupFilterList("buildorderallocation", $(table));
$(table).inventreeTable({
url: '{% url "api-build-item-list" %}',
queryParams: filters,
name: 'buildorderallocation',
groupBy: false,
search: false,
paginationVAlign: 'bottom',
original: options.params,
formatNoMatches: function() {
return '{% trans "No build order allocations found" %}'
},
columns: [
{
field: 'pk',
visible: false,
switchable: false,
},
{
field: 'build',
switchable: false,
title: '{% trans "Build Order" %}',
formatter: function(value, row) {
var prefix = "{% settings_value 'BUILDORDER_REFERENCE_PREFIX' %}";
var ref = `${prefix}${row.build_detail.reference}`;
return renderLink(ref, `/build/${row.build}/`);
}
},
{
field: 'item',
title: '{% trans "Stock Item" %}',
formatter: function(value, row) {
// Render a link to the particular stock item
var link = `/stock/item/${row.stock_item}/`;
var text = `{% trans "Stock Item" %} ${row.stock_item}`;
return renderLink(text, link);
}
},
{
field: 'location',
title: '{% trans "Location" %}',
formatter: function(value, row) {
if (!value) {
return '{% trans "Location not specified" %}';
}
var link = `/stock/location/${value}`;
var text = row.location_detail.description;
return renderLink(text, link);
}
},
{
field: 'quantity',
title: '{% trans "Quantity" %}',
}
]
});
}
function loadBuildOutputAllocationTable(buildInfo, output, options={}) { function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
/* /*
* Load the "allocation table" for a particular build output. * Load the "allocation table" for a particular build output.
@ -347,6 +429,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
var params = { var params = {
build: buildId, build: buildId,
part_detail: true,
location_detail: true,
} }
if (output) { if (output) {
@ -466,8 +550,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
title: '{% trans "Part" %}', title: '{% trans "Part" %}',
formatter: function(value, row) { formatter: function(value, row) {
var html = imageHoverIcon(row.part_thumb); var html = imageHoverIcon(row.part_detail.thumbnail);
html += renderLink(row.part_name, `/part/${value}/`); html += renderLink(row.part_detail.full_name, `/part/${value}/`);
return html; return html;
} }
}, },

View File

@ -310,3 +310,88 @@ function loadSalesOrderTable(table, options) {
], ],
}); });
} }
function loadSalesOrderAllocationTable(table, options={}) {
/**
* Load a table with SalesOrderAllocation items
*/
options.params = options.params || {};
options.params['location_detail'] = true;
options.params['part_detail'] = true;
options.params['item_detail'] = true;
options.params['order_detail'] = true;
var filters = loadTableFilters("salesorderallocation");
for (var key in options.params) {
filters[key] = options.params[key];
}
setupFilterList("salesorderallocation", $(table));
$(table).inventreeTable({
url: '{% url "api-so-allocation-list" %}',
queryParams: filters,
name: 'salesorderallocation',
groupBy: false,
search: false,
paginationVAlign: 'bottom',
original: options.params,
formatNoMatches: function() { return '{% trans "No sales order allocations found" %}'; },
columns: [
{
field: 'pk',
visible: false,
switchable: false,
},
{
field: 'order',
switchable: false,
title: '{% trans "Order" %}',
switchable: false,
formatter: function(value, row) {
var prefix = "{% settings_value 'SALESORDER_REFERENCE_PREFIX' %}";
var ref = `${prefix}${row.order_detail.reference}`;
return renderLink(ref, `/order/sales-order/${row.order}/`);
}
},
{
field: 'item',
title: '{% trans "Stock Item" %}',
formatter: function(value, row) {
// Render a link to the particular stock item
var link = `/stock/item/${row.item}/`;
var text = `{% trans "Stock Item" %} ${row.item}`;
return renderLink(text, link);
}
},
{
field: 'location',
title: '{% trans "Location" %}',
formatter: function(value, row) {
if (!value) {
return '{% trans "Location not specified" %}';
}
var link = `/stock/location/${value}`;
var text = row.location_detail.description;
return renderLink(text, link);
}
},
{
field: 'quantity',
title: '{% trans "Quantity" %}',
}
]
});
}

View File

@ -135,7 +135,7 @@ $.fn.inventreeTable = function(options) {
// Pagingation options (can be server-side or client-side as specified by the caller) // Pagingation options (can be server-side or client-side as specified by the caller)
options.pagination = true; options.pagination = true;
options.paginationVAlign = 'both'; options.paginationVAlign = options.paginationVAlign || 'both';
options.pageSize = inventreeLoad(varName, 25); options.pageSize = inventreeLoad(varName, 25);
options.pageList = [25, 50, 100, 250, 'all']; options.pageList = [25, 50, 100, 250, 'all'];
options.totalField = 'count'; options.totalField = 'count';

View File

@ -129,6 +129,14 @@ def wait(c):
manage(c, "wait_for_db") manage(c, "wait_for_db")
@task
def rebuild(c):
"""
Rebuild database models with MPTT structures
"""
manage(c, "rebuild_models")
@task @task
def migrate(c): def migrate(c):
""" """
@ -243,12 +251,15 @@ def content_excludes():
"contenttypes", "contenttypes",
"sessions.session", "sessions.session",
"auth.permission", "auth.permission",
"authtoken.token",
"error_report.error", "error_report.error",
"admin.logentry", "admin.logentry",
"django_q.schedule", "django_q.schedule",
"django_q.task", "django_q.task",
"django_q.ormq", "django_q.ormq",
"users.owner", "users.owner",
"exchange.rate",
"exchange.exchangebackend",
] ]
output = "" output = ""
@ -311,7 +322,7 @@ def export_records(c, filename='data.json'):
print("Data export completed") print("Data export completed")
@task(help={'filename': 'Input filename'}) @task(help={'filename': 'Input filename'}, post=[rebuild])
def import_records(c, filename='data.json'): def import_records(c, filename='data.json'):
""" """
Import database records from a file Import database records from a file
@ -354,7 +365,7 @@ def import_records(c, filename='data.json'):
print("Data import completed") print("Data import completed")
@task @task(post=[rebuild])
def import_fixtures(c): def import_fixtures(c):
""" """
Import fixture data into the database. Import fixture data into the database.