mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-17 20:45:44 +00:00
[Feature] Add RMA support (#4488)
* Adds ReturnOrder and ReturnOrderAttachment models
* Adds new 'role' specific for return orders
* Refactor total_price into a mixin
- Required for PurchaseOrder and SalesOrder
- May not be required for ReturnOrder (remains to be seen)
* Adds API endpoints for ReturnOrder
- Add list endpoint
- Add detail endpoint
- Adds required serializer models
* Adds basic "index" page for Return Order model
* Update API version
* Update navbar text
* Add db migration for new "role"
* Add ContactList and ContactDetail API endpoints
* Adds template and JS code for manipulation of contacts
- Display a table
- Create / edit / delete
* Splits order.js into multiple files
- Javascript files was becoming extremely large
- Hard to debug and find code
- Split into purchase_order / return_order / sales_order
* Fix role name (change 'returns' to 'return_order')
- Similar to existing roles for purchase_order and sales_order
* Adds detail page for ReturnOrder
* URL cleanup
- Use <int:pk> instead of complex regex
* More URL cleanup
* Add "return orders" list to company detail page
* Break JS status codes into new javascript file
- Always difficult to track down where these are rendered
- Enough to warrant their own file now
* Add ability to edit return order from detail page
* Database migrations
- Add new ReturnOrder modeles
- Add new 'contact' field to external orders
* Adds "contact" to ReturnOrder
- Implement check to ensure that the selected "contact" matches the selected "company"
* Adjust filters to limit contact options
* Fix typo
* Expose 'contact' field for PurchaseOrder model
* Render contact information
* Add "contact" for SalesOrder
* Adds setting to enable / disable return order functionality
- Simply hides the navigation elements
- API is not disabled
* Support filtering ReturnOrder by 'status'
- Refactors existing filter into the OrderFilter class
* js linting
* More JS linting
* Adds ReturnOrderReport model
* Add serializer for the ReturnOrderReport model
- A little bit of refactoring along the way
* Admin integration for new report model
* Refactoring for report.api
- Adds generic mixins for filtering queryset (based on updates to label.api)
- Reduces repeated code a *lot*
* Exposes API endpoints for ReturnOrderReport
* Adds default example report file for ReturnOrder
- Requires some more work :)
* Refactor report printing javascript code
- Replace all existing functions with 'printReports'
* Improvements for default StockItem test report template
- Fix bug in template
- Handle potential errors in template tags
- Add more helpers to report tags
- Improve test result rendering
* Reduce logging verbosity from weasyprint
* Refactor javascript for label printing
- Consolidate into a single function
- Similar to refactor of report functions
* Add report print button to return order page
* Record user reference when creating via API
* Refactor order serializers
- Move common code into AbstractOrderSerializer class
* Adds extra line item model for the return order
- Adds serializer and API endpoints as appropriate
* Render extra line table for return order
- Refactor existing functions into a single generic function
- Reduces repeated JS code a lot
* Add ability to create a new extra line item
* Adds button for creating a new lien item
* JS linting
* Update test
* Typo fix
(cherry picked from commit 28ac2be35b
)
* Enable search for return order
* Don't do pricing (yet) for returnorder extra line table
- Fixes an uncaught error
* Error catching for api.js
* Updates for order models:
- Add 'target_date' field to abstract Order model
- Add IN_PROGRESS status code for return order
- Refactor 'overdue' and 'outstanding' API queries
- Refactor OVERDUE_FILTER on order models
- Refactor is_overdue on order models
- More table filters for return order model
* JS cleanup
* Create ReturnOrderLineItem model
- New type of status label
- Add TotalPriceMixin to ReturnOrder model
* Adds an API serializer for the ReturnOrderLineItem model
* Add API endpoints for ReturnOrderLineItem model
- Including some refactoring along the way
* javascript: refactor loadTableFilters function
- Pass enforced query through to the filters
- Call Object.assign() to construct a superset query
- Removes a lot of code duplication
* Refactor hard-coded URLS to use {% url %} lookup
- Forces error if the URL is wrong
- If we ever change the URL, will still work
* Implement creation of new return order line items
* Adds 'part_detail' annotation to ReturnOrderLineItem serializer
- Required for rendering part information
* javascript: refactor method for creating a group of buttons in a table
* javascript: refactor common buttons with helper functions
* Allow edit and delete of return order line items
* Add form option to automatically reload a table on success
- Pass table name to options.refreshTable
* JS linting
* Add common function for createExtraLineItem
* Refactor loading of attachment tables
- Setup drag-and-drop as part of core function
* CI fixes
* Refactoring out some more common API endpoint code
* Update migrations
* Fix permission typo
* Refactor for unit testing code
* Add unit tests for Contact model
* Tests for returnorder list API
* Annotate 'line_items' to ReturnOrder serializer
* Driving the refactor tractor
* More unit tests for the ReturnOrder API endpoints
* Refactor "print orders" button for various order tables
- Move into "setupFilterList" code (generic)
* add generic 'label printing' button to table actions buttons
* Refactor build output table
* Refactoring icon generation for js
* Refactoring for Part API
* Fix database model type for 'received_date'
* Add API endpoint to "issue" a ReturnOrder
* Improvements for stock tracking table
- Add new status codes
- Add rendering for SalesOrder
- Add rendering for ReturnOrder
- Fix status badges
* Adds functionality to receive line items against a return order
* Add endpoints for completing and cancelling orders
* Add option to allow / prevent editing of ReturnOrder after completed
* js linting
* Wrap "add extra line" button in setting check
* Updates to order/admin.py
* Remove inline admin for returnorderline model
* Updates to pass CI
* Serializer fix
* order template fixes
* Unit test fix
* Fixes for ReturnOrder.receive_line_item
* Unit testing for receiving line items against an RMA
* Improve example report for return order
* Extend unit tests for reporting
* Cleanup here and there
* Unit testing for order views
* Clear "sales_order" field when returning against ReturnOrder
* Add 'location' to deltas when returning from customer
* Bug fix for unit test
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
"""JSON API for the Build app."""
|
||||
|
||||
from django.urls import include, re_path
|
||||
from django.urls import include, path, re_path
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
@ -509,13 +509,13 @@ build_api_urls = [
|
||||
|
||||
# Attachments
|
||||
re_path(r'^attachment/', include([
|
||||
re_path(r'^(?P<pk>\d+)/', BuildAttachmentDetail.as_view(), name='api-build-attachment-detail'),
|
||||
path(r'<int:pk>/', BuildAttachmentDetail.as_view(), name='api-build-attachment-detail'),
|
||||
re_path(r'^.*$', BuildAttachmentList.as_view(), name='api-build-attachment-list'),
|
||||
])),
|
||||
|
||||
# Build Items
|
||||
re_path(r'^item/', include([
|
||||
re_path(r'^(?P<pk>\d+)/', include([
|
||||
path(r'<int:pk>/', include([
|
||||
re_path(r'^metadata/', BuildItemMetadata.as_view(), name='api-build-item-metadata'),
|
||||
re_path(r'^.*$', BuildItemDetail.as_view(), name='api-build-item-detail'),
|
||||
])),
|
||||
@ -523,7 +523,7 @@ build_api_urls = [
|
||||
])),
|
||||
|
||||
# Build Detail
|
||||
re_path(r'^(?P<pk>\d+)/', include([
|
||||
path(r'<int:pk>/', include([
|
||||
re_path(r'^allocate/', BuildAllocate.as_view(), name='api-build-allocate'),
|
||||
re_path(r'^auto-allocate/', BuildAutoAllocate.as_view(), name='api-build-auto-allocate'),
|
||||
re_path(r'^complete/', BuildOutputComplete.as_view(), name='api-build-output-complete'),
|
||||
|
@ -36,9 +36,10 @@ from plugin.events import trigger_event
|
||||
from plugin.models import MetadataMixin
|
||||
|
||||
import common.notifications
|
||||
from part import models as PartModels
|
||||
from stock import models as StockModels
|
||||
from users import models as UserModels
|
||||
|
||||
import part.models
|
||||
import stock.models
|
||||
import users.models
|
||||
|
||||
|
||||
class Build(MPTTModel, MetadataMixin, ReferenceIndexingMixin):
|
||||
@ -279,7 +280,7 @@ class Build(MPTTModel, MetadataMixin, ReferenceIndexingMixin):
|
||||
)
|
||||
|
||||
responsible = models.ForeignKey(
|
||||
UserModels.Owner,
|
||||
users.models.Owner,
|
||||
on_delete=models.SET_NULL,
|
||||
blank=True, null=True,
|
||||
verbose_name=_('Responsible'),
|
||||
@ -395,9 +396,9 @@ class Build(MPTTModel, MetadataMixin, ReferenceIndexingMixin):
|
||||
|
||||
if in_stock is not None:
|
||||
if in_stock:
|
||||
outputs = outputs.filter(StockModels.StockItem.IN_STOCK_FILTER)
|
||||
outputs = outputs.filter(stock.models.StockItem.IN_STOCK_FILTER)
|
||||
else:
|
||||
outputs = outputs.exclude(StockModels.StockItem.IN_STOCK_FILTER)
|
||||
outputs = outputs.exclude(stock.models.StockItem.IN_STOCK_FILTER)
|
||||
|
||||
# Filter by 'complete' status
|
||||
complete = kwargs.get('complete', None)
|
||||
@ -659,7 +660,7 @@ class Build(MPTTModel, MetadataMixin, ReferenceIndexingMixin):
|
||||
else:
|
||||
serial = None
|
||||
|
||||
output = StockModels.StockItem.objects.create(
|
||||
output = stock.models.StockItem.objects.create(
|
||||
quantity=1,
|
||||
location=location,
|
||||
part=self.part,
|
||||
@ -677,11 +678,11 @@ class Build(MPTTModel, MetadataMixin, ReferenceIndexingMixin):
|
||||
|
||||
parts = bom_item.get_valid_parts_for_allocation()
|
||||
|
||||
items = StockModels.StockItem.objects.filter(
|
||||
items = stock.models.StockItem.objects.filter(
|
||||
part__in=parts,
|
||||
serial=str(serial),
|
||||
quantity=1,
|
||||
).filter(StockModels.StockItem.IN_STOCK_FILTER)
|
||||
).filter(stock.models.StockItem.IN_STOCK_FILTER)
|
||||
|
||||
"""
|
||||
Test if there is a matching serial number!
|
||||
@ -701,7 +702,7 @@ class Build(MPTTModel, MetadataMixin, ReferenceIndexingMixin):
|
||||
else:
|
||||
"""Create a single build output of the given quantity."""
|
||||
|
||||
StockModels.StockItem.objects.create(
|
||||
stock.models.StockItem.objects.create(
|
||||
quantity=quantity,
|
||||
location=location,
|
||||
part=self.part,
|
||||
@ -877,7 +878,7 @@ class Build(MPTTModel, MetadataMixin, ReferenceIndexingMixin):
|
||||
)
|
||||
|
||||
# Look for available stock items
|
||||
available_stock = StockModels.StockItem.objects.filter(StockModels.StockItem.IN_STOCK_FILTER)
|
||||
available_stock = stock.models.StockItem.objects.filter(stock.models.StockItem.IN_STOCK_FILTER)
|
||||
|
||||
# Filter by list of available parts
|
||||
available_stock = available_stock.filter(
|
||||
@ -1220,7 +1221,7 @@ class BuildItem(MetadataMixin, models.Model):
|
||||
'quantity': _('Quantity must be 1 for serialized stock')
|
||||
})
|
||||
|
||||
except (StockModels.StockItem.DoesNotExist, PartModels.Part.DoesNotExist):
|
||||
except (stock.models.StockItem.DoesNotExist, part.models.Part.DoesNotExist):
|
||||
pass
|
||||
|
||||
"""
|
||||
@ -1259,8 +1260,8 @@ class BuildItem(MetadataMixin, models.Model):
|
||||
for idx, ancestor in enumerate(ancestors):
|
||||
|
||||
try:
|
||||
bom_item = PartModels.BomItem.objects.get(part=self.build.part, sub_part=ancestor)
|
||||
except PartModels.BomItem.DoesNotExist:
|
||||
bom_item = part.models.BomItem.objects.get(part=self.build.part, sub_part=ancestor)
|
||||
except part.models.BomItem.DoesNotExist:
|
||||
continue
|
||||
|
||||
# A matching BOM item has been found!
|
||||
@ -1350,7 +1351,7 @@ class BuildItem(MetadataMixin, models.Model):
|
||||
# Internal model which links part <-> sub_part
|
||||
# We need to track this separately, to allow for "variant' stock
|
||||
bom_item = models.ForeignKey(
|
||||
PartModels.BomItem,
|
||||
part.models.BomItem,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='allocate_build_items',
|
||||
blank=True, null=True,
|
||||
|
@ -247,7 +247,11 @@ src="{% static 'img/blank_image.png' %}"
|
||||
|
||||
{% if report_enabled %}
|
||||
$('#print-build-report').click(function() {
|
||||
printBuildReports([{{ build.pk }}]);
|
||||
printReports({
|
||||
items: [{{ build.pk }}],
|
||||
key: 'build',
|
||||
url: '{% url "api-build-report-list" %}',
|
||||
});
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
|
@ -268,19 +268,6 @@
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- Label Printing Actions -->
|
||||
<div class='btn-group'>
|
||||
<button id='output-print-options' class='btn btn-primary dropdown-toggle' type='button' data-bs-toggle='dropdown' title='{% trans "Printing Actions" %}'>
|
||||
<span class='fas fa-print'></span> <span class='caret'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu'>
|
||||
<li><a class='dropdown-item' href='#' id='incomplete-output-print-label' title='{% trans "Print labels" %}'>
|
||||
<span class='fas fa-tags'></span> {% trans "Print labels" %}
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{% include "filter_list.html" with id='incompletebuilditems' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
@ -367,20 +354,6 @@ onPanelLoad('children', function() {
|
||||
|
||||
onPanelLoad('attachments', function() {
|
||||
|
||||
enableDragAndDrop(
|
||||
'#attachment-dropzone',
|
||||
'{% url "api-build-attachment-list" %}',
|
||||
{
|
||||
data: {
|
||||
build: {{ build.id }},
|
||||
},
|
||||
label: 'attachment',
|
||||
success: function(data, status, xhr) {
|
||||
$('#attachment-table').bootstrapTable('refresh');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
loadAttachmentTable('{% url "api-build-attachment-list" %}', {
|
||||
filters: {
|
||||
build: {{ build.pk }},
|
||||
@ -409,10 +382,6 @@ onPanelLoad('notes', function() {
|
||||
);
|
||||
});
|
||||
|
||||
function reloadTable() {
|
||||
$('#allocation-table-untracked').bootstrapTable('refresh');
|
||||
}
|
||||
|
||||
onPanelLoad('outputs', function() {
|
||||
{% if build.active %}
|
||||
|
||||
|
@ -26,20 +26,6 @@
|
||||
<div id='button-toolbar'>
|
||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||
<div class='btn-group' role='group'>
|
||||
|
||||
{% if report_enabled %}
|
||||
<div class='btn-group' role='group'>
|
||||
<!-- Print actions -->
|
||||
<button id='build-print-options' class='btn btn-primary dropdown-toggle' data-bs-toggle='dropdown'>
|
||||
<span class='fas fa-print'></span> <span class='caret'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu'>
|
||||
<li><a class='dropdown-item' href='#' id='multi-build-print' title='{% trans "Print Build Orders" %}'>
|
||||
<span class='fas fa-file-pdf'></span> {% trans "Print Build Orders" %}
|
||||
</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% include "filter_list.html" with id="build" %}
|
||||
</div>
|
||||
</div>
|
||||
@ -62,17 +48,4 @@ loadBuildTable($("#build-table"), {
|
||||
locale: '{{ request.LANGUAGE_CODE }}',
|
||||
});
|
||||
|
||||
{% if report_enabled %}
|
||||
$('#multi-build-print').click(function() {
|
||||
var rows = getTableData("#build-table");
|
||||
var build_ids = [];
|
||||
|
||||
rows.forEach(function(row) {
|
||||
build_ids.push(row.pk);
|
||||
});
|
||||
|
||||
printBuildReports(build_ids);
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
@ -1,13 +1,13 @@
|
||||
"""URL lookup for Build app."""
|
||||
|
||||
from django.urls import include, re_path
|
||||
from django.urls import include, path, re_path
|
||||
|
||||
from . import views
|
||||
|
||||
|
||||
build_urls = [
|
||||
|
||||
re_path(r'^(?P<pk>\d+)/', include([
|
||||
path(r'<int:pk>/', include([
|
||||
re_path(r'^.*$', views.BuildDetail.as_view(), name='build-detail'),
|
||||
])),
|
||||
|
||||
|
Reference in New Issue
Block a user