2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-05-06 07:18:48 +00:00
Oliver 27aa16d55d
[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 28ac2be35bd0148c598629988d40b1a234f069a5)

* 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
2023-03-29 10:35:43 +11:00

290 lines
7.3 KiB
JavaScript

{% load i18n %}
{% load inventree_extras %}
/* globals
*/
/* exported
inventreeGet,
inventreeDelete,
inventreeFormDataUpload,
showApiError,
*/
$.urlParam = function(name) {
// eslint-disable-next-line no-useless-escape
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (results == null) {
return null;
}
return decodeURI(results[1]) || 0;
};
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) == (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
/*
* Perform a GET request to the InvenTree server
*/
function inventreeGet(url, filters={}, options={}) {
if (!url) {
console.error('inventreeGet called without url');
return;
}
// Middleware token required for data update
var csrftoken = getCookie('csrftoken');
return $.ajax({
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRFToken', csrftoken);
},
url: url,
type: 'GET',
data: filters,
dataType: 'json',
contentType: 'application/json',
async: (options.async == false) ? false : true,
success: function(response) {
if (options.success) {
options.success(response);
}
},
error: function(xhr, ajaxOptions, thrownError) {
console.error('Error on GET at ' + url);
if (thrownError) {
console.error('Error: ' + thrownError);
}
if (options.error) {
options.error({
error: thrownError
});
} else {
showApiError(xhr, url);
}
}
});
}
/* Upload via AJAX using the FormData approach.
*
* Note that the following AJAX parameters are required for FormData upload
*
* processData: false
* contentType: false
*/
function inventreeFormDataUpload(url, data, options={}) {
if (!url) {
console.error('inventreeFormDataUpload called without url');
return;
}
// CSRF cookie token
var csrftoken = getCookie('csrftoken');
return $.ajax({
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRFToken', csrftoken);
},
url: url,
method: options.method || 'POST',
data: data,
processData: false,
contentType: false,
success: function(data, status, xhr) {
if (options.success) {
options.success(data, status, xhr);
}
},
error: function(xhr, status, error) {
console.error('Form data upload failure: ' + status);
if (options.error) {
options.error(xhr, status, error);
} else {
showApiError(xhr, url);
}
}
});
}
/*
* Perform a PUT or PATCH request to the InvenTree server
*/
function inventreePut(url, data={}, options={}) {
if (!url) {
console.error('inventreePut called without url');
return;
}
var method = options.method || 'PUT';
// Middleware token required for data update
var csrftoken = getCookie('csrftoken');
return $.ajax({
beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRFToken', csrftoken);
},
url: url,
type: method,
data: JSON.stringify(data),
dataType: 'json',
contentType: 'application/json',
success: function(response, status) {
if (options.success) {
options.success(response, status);
}
if (options.reloadOnSuccess) {
location.reload();
}
},
error: function(xhr, ajaxOptions, thrownError) {
if (options.error) {
options.error(xhr, ajaxOptions, thrownError);
} else {
console.error(`Error on ${method} to '${url}' - STATUS ${xhr.status}`);
console.error(thrownError);
showApiError(xhr, url);
}
},
complete: function(xhr, status) {
if (options.complete) {
options.complete(xhr, status);
}
}
});
}
/*
* Performs a DELETE API call to the server
*/
function inventreeDelete(url, options={}) {
if (!url) {
console.error('inventreeDelete called without url');
return;
}
options = options || {};
options.method = 'DELETE';
return inventreePut(
url,
options.data || {},
options
);
}
/*
* Display a notification with error information
*/
function showApiError(xhr, url) {
var title = null;
var message = null;
if (xhr.statusText == 'abort') {
// Don't show errors for requests which were intentionally aborted
return;
}
switch (xhr.status || 0) {
// No response
case 0:
title = '{% trans "No Response" %}';
message = '{% trans "No response from the InvenTree server" %}';
break;
// Bad request
case 400:
// Note: Normally error code 400 is handled separately,
// and should now be shown here!
title = '{% trans "Error 400: Bad request" %}';
message = '{% trans "API request returned error code 400" %}';
break;
// Not authenticated
case 401:
title = '{% trans "Error 401: Not Authenticated" %}';
message = '{% trans "Authentication credentials not supplied" %}';
break;
// Permission denied
case 403:
title = '{% trans "Error 403: Permission Denied" %}';
message = '{% trans "You do not have the required permissions to access this function" %}';
break;
// Resource not found
case 404:
title = '{% trans "Error 404: Resource Not Found" %}';
message = '{% trans "The requested resource could not be located on the server" %}';
break;
// Method not allowed
case 405:
title = '{% trans "Error 405: Method Not Allowed" %}';
message = '{% trans "HTTP method not allowed at URL" %}';
break;
// Timeout
case 408:
title = '{% trans "Error 408: Timeout" %}';
message = '{% trans "Connection timeout while requesting data from server" %}';
break;
default:
title = '{% trans "Unhandled Error Code" %}';
message = `{% trans "Error code" %}: ${xhr.status}`;
var response = xhr.responseJSON;
// The server may have provided some extra information about this error
if (response) {
if (response.error) {
title = response.error;
}
if (response.detail) {
message = response.detail;
}
}
break;
}
if (url) {
message += '<hr>';
message += `URL: ${url}`;
}
showMessage(title, {
style: 'danger',
icon: 'fas fa-server icon-red',
details: message,
});
}