mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 13:05:42 +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:
@ -75,9 +75,7 @@ $('#history-delete').click(function() {
|
||||
multi_delete: true,
|
||||
preFormContent: html,
|
||||
title: '{% trans "Delete Notifications" %}',
|
||||
onSuccess: function() {
|
||||
$('#history-table').bootstrapTable('refresh');
|
||||
},
|
||||
refreshTable: '#history-table',
|
||||
form_data: {
|
||||
filters: {
|
||||
read: true,
|
||||
@ -88,7 +86,7 @@ $('#history-delete').click(function() {
|
||||
});
|
||||
|
||||
$("#history-table").on('click', '.notification-delete', function() {
|
||||
constructForm(`/api/notifications/${$(this).attr('pk')}/`, {
|
||||
constructForm(`{% url "api-notifications-list" %}${$(this).attr('pk')}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Notification" %}',
|
||||
onSuccess: function(data) {
|
||||
|
20
InvenTree/templates/InvenTree/settings/returns.html
Normal file
20
InvenTree/templates/InvenTree/settings/returns.html
Normal file
@ -0,0 +1,20 @@
|
||||
{% extends "panel.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block label %}return-order{% endblock %}
|
||||
|
||||
{% block heading %}
|
||||
{% trans "Return Order Settings" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="RETURNORDER_ENABLED" icon="fa-check-circle" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="RETURNORDER_REFERENCE_PATTERN" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="RETURNORDER_EDIT_COMPLETED_ORDERS" icon="fa-edit" %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{% endblock %}
|
@ -42,6 +42,7 @@
|
||||
{% include "InvenTree/settings/build.html" %}
|
||||
{% include "InvenTree/settings/po.html" %}
|
||||
{% include "InvenTree/settings/so.html" %}
|
||||
{% include "InvenTree/settings/returns.html" %}
|
||||
|
||||
{% include "InvenTree/settings/plugin.html" %}
|
||||
{% plugin_list as pl_list %}
|
||||
|
@ -284,9 +284,7 @@ onPanelLoad('parts', function() {
|
||||
},
|
||||
method: 'POST',
|
||||
title: '{% trans "Create Part Parameter Template" %}',
|
||||
onSuccess: function() {
|
||||
$("#param-table").bootstrapTable('refresh');
|
||||
},
|
||||
refreshTable: '#param-table',
|
||||
});
|
||||
});
|
||||
|
||||
@ -303,9 +301,7 @@ onPanelLoad('parts', function() {
|
||||
description: {},
|
||||
},
|
||||
title: '{% trans "Edit Part Parameter Template" %}',
|
||||
onSuccess: function() {
|
||||
$("#param-table").bootstrapTable('refresh');
|
||||
},
|
||||
refreshTable: '#param-table',
|
||||
}
|
||||
);
|
||||
});
|
||||
@ -325,9 +321,7 @@ onPanelLoad('parts', function() {
|
||||
method: 'DELETE',
|
||||
preFormContent: html,
|
||||
title: '{% trans "Delete Part Parameter Template" %}',
|
||||
onSuccess: function() {
|
||||
$("#param-table").bootstrapTable('refresh');
|
||||
},
|
||||
refreshTable: '#param-table',
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -52,6 +52,8 @@
|
||||
{% include "sidebar_item.html" with label='purchase-order' text=text icon="fa-shopping-cart" %}
|
||||
{% trans "Sales Orders" as text %}
|
||||
{% include "sidebar_item.html" with label='sales-order' text=text icon="fa-truck" %}
|
||||
{% trans "Return Orders" as text %}
|
||||
{% include "sidebar_item.html" with label='return-order' text=text icon="fa-undo" %}
|
||||
|
||||
{% trans "Plugin Settings" as text %}
|
||||
{% include "sidebar_header.html" with text=text %}
|
||||
|
@ -28,6 +28,8 @@
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_EXCLUDE_INACTIVE_PURCHASE_ORDERS" user_setting=True icon='fa-eye-slash' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_SHOW_SALES_ORDERS" user_setting=True icon='fa-truck' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_EXCLUDE_INACTIVE_SALES_ORDERS" user_setting=True icon='fa-eye-slash' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_SHOW_RETURN_ORDERS" user_setting=True icon='fa-truck' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_EXCLUDE_INACTIVE_RETURN_ORDERS" user_setting=True icon='fa-eye-slash' %}
|
||||
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_RESULTS" user_setting=True icon='fa-search' %}
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
{% plugins_enabled as plugins_enabled %}
|
||||
{% settings_value 'BARCODE_ENABLE' as barcodes %}
|
||||
{% settings_value 'REPORT_ENABLE_TEST_REPORT' as test_report_enabled %}
|
||||
{% settings_value 'RETURNORDER_ENABLED' as return_order_enabled %}
|
||||
{% settings_value "REPORT_ENABLE" as report_enabled %}
|
||||
{% settings_value "SERVER_RESTART_REQUIRED" as server_restart_required %}
|
||||
{% settings_value "LABEL_ENABLE" as labels_enabled %}
|
||||
@ -164,8 +165,12 @@
|
||||
<script defer type='text/javascript' src="{% i18n_static 'model_renderers.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% i18n_static 'order.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% i18n_static 'part.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% i18n_static 'purchase_order.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% i18n_static 'return_order.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% i18n_static 'report.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% i18n_static 'sales_order.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% i18n_static 'search.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% i18n_static 'status_codes.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% i18n_static 'stock.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% i18n_static 'plugin.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% i18n_static 'pricing.js' %}"></script>
|
||||
|
11
InvenTree/templates/email/return_order_received.html
Normal file
11
InvenTree/templates/email/return_order_received.html
Normal file
@ -0,0 +1,11 @@
|
||||
{% extends "email/email.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% block title %}
|
||||
{{ message }}
|
||||
{% if link %}
|
||||
<p>{% trans "Click on the following link to view this order" %}: <a href='{{ link }}'>{{ link }}</a></p>
|
||||
{% endif %}
|
||||
{% endblock title %}
|
@ -14,18 +14,24 @@
|
||||
* Helper functions for calendar display
|
||||
*/
|
||||
|
||||
/*
|
||||
* Extract the first displayed date on the calendar
|
||||
*/
|
||||
function startDate(calendar) {
|
||||
// Extract the first displayed date on the calendar
|
||||
return calendar.currentData.dateProfile.activeRange.start.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract the last display date on the calendar
|
||||
*/
|
||||
function endDate(calendar) {
|
||||
// Extract the last display date on the calendar
|
||||
return calendar.currentData.dateProfile.activeRange.end.toISOString().split('T')[0];
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all events from the calendar
|
||||
*/
|
||||
function clearEvents(calendar) {
|
||||
// Remove all events from the calendar
|
||||
|
||||
var events = calendar.getEvents();
|
||||
|
||||
|
@ -40,8 +40,17 @@ function getCookie(name) {
|
||||
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');
|
||||
|
||||
@ -78,14 +87,20 @@ function inventreeGet(url, filters={}, options={}) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* 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={}) {
|
||||
/* Upload via AJAX using the FormData approach.
|
||||
*
|
||||
* Note that the following AJAX parameters are required for FormData upload
|
||||
*
|
||||
* processData: false
|
||||
* contentType: false
|
||||
*/
|
||||
|
||||
if (!url) {
|
||||
console.error('inventreeFormDataUpload called without url');
|
||||
return;
|
||||
}
|
||||
|
||||
// CSRF cookie token
|
||||
var csrftoken = getCookie('csrftoken');
|
||||
@ -116,8 +131,17 @@ function inventreeFormDataUpload(url, data, options={}) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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
|
||||
@ -164,6 +188,11 @@ function inventreePut(url, data={}, options={}) {
|
||||
*/
|
||||
function inventreeDelete(url, options={}) {
|
||||
|
||||
if (!url) {
|
||||
console.error('inventreeDelete called without url');
|
||||
return;
|
||||
}
|
||||
|
||||
options = options || {};
|
||||
|
||||
options.method = 'DELETE';
|
||||
|
@ -1,15 +1,14 @@
|
||||
{% load i18n %}
|
||||
|
||||
/* globals
|
||||
makeIconButton,
|
||||
renderLink,
|
||||
wrapButtons,
|
||||
*/
|
||||
|
||||
/* exported
|
||||
attachmentLink,
|
||||
addAttachmentButtonCallbacks,
|
||||
loadAttachmentTable,
|
||||
reloadAttachmentTable,
|
||||
loadAttachmentTable
|
||||
*/
|
||||
|
||||
|
||||
@ -35,7 +34,7 @@ function addAttachmentButtonCallbacks(url, fields={}) {
|
||||
constructForm(url, {
|
||||
fields: file_fields,
|
||||
method: 'POST',
|
||||
onSuccess: reloadAttachmentTable,
|
||||
refreshTable: '#attachment-table',
|
||||
title: '{% trans "Add Attachment" %}',
|
||||
});
|
||||
});
|
||||
@ -57,7 +56,7 @@ function addAttachmentButtonCallbacks(url, fields={}) {
|
||||
constructForm(url, {
|
||||
fields: link_fields,
|
||||
method: 'POST',
|
||||
onSuccess: reloadAttachmentTable,
|
||||
refreshTable: '#attachment-table',
|
||||
title: '{% trans "Add Link" %}',
|
||||
});
|
||||
});
|
||||
@ -79,9 +78,9 @@ function deleteAttachments(attachments, url, options={}) {
|
||||
var icon = '';
|
||||
|
||||
if (attachment.filename) {
|
||||
icon = `<span class='fas fa-file-alt'></span>`;
|
||||
icon = makeIcon(attachmentIcon(attachment.filename), '');
|
||||
} else if (attachment.link) {
|
||||
icon = `<span class='fas fa-link'></span>`;
|
||||
icon = makeIcon('fa-link', '');
|
||||
}
|
||||
|
||||
return `
|
||||
@ -123,29 +122,15 @@ function deleteAttachments(attachments, url, options={}) {
|
||||
items: ids,
|
||||
filters: options.filters,
|
||||
},
|
||||
onSuccess: function() {
|
||||
// Refresh the table once all attachments are deleted
|
||||
$('#attachment-table').bootstrapTable('refresh');
|
||||
}
|
||||
refreshTable: '#attachment-table',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function reloadAttachmentTable() {
|
||||
|
||||
$('#attachment-table').bootstrapTable('refresh');
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Render a link (with icon) to an internal attachment (file)
|
||||
* Return a particular icon based on filename extension
|
||||
*/
|
||||
function attachmentLink(filename) {
|
||||
|
||||
if (!filename) {
|
||||
return null;
|
||||
}
|
||||
|
||||
function attachmentIcon(filename) {
|
||||
// Default file icon (if no better choice is found)
|
||||
let icon = 'fa-file-alt';
|
||||
let fn = filename.toLowerCase();
|
||||
@ -171,10 +156,25 @@ function attachmentLink(filename) {
|
||||
});
|
||||
}
|
||||
|
||||
let split = filename.split('/');
|
||||
fn = split[split.length - 1];
|
||||
return icon;
|
||||
}
|
||||
|
||||
let html = `<span class='fas ${icon}'></span> ${fn}`;
|
||||
|
||||
/*
|
||||
* Render a link (with icon) to an internal attachment (file)
|
||||
*/
|
||||
function attachmentLink(filename) {
|
||||
|
||||
if (!filename) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let split = filename.split('/');
|
||||
let fn = split[split.length - 1];
|
||||
|
||||
let icon = attachmentIcon(filename);
|
||||
|
||||
let html = makeIcon(icon) + ` ${fn}`;
|
||||
|
||||
return renderLink(html, filename, {download: true});
|
||||
|
||||
@ -271,7 +271,7 @@ function loadAttachmentTable(url, options) {
|
||||
delete opts.fields.link;
|
||||
}
|
||||
},
|
||||
onSuccess: reloadAttachmentTable,
|
||||
refreshTable: '#attachment-table',
|
||||
title: '{% trans "Edit Attachment" %}',
|
||||
});
|
||||
});
|
||||
@ -299,7 +299,7 @@ function loadAttachmentTable(url, options) {
|
||||
if (row.attachment) {
|
||||
return attachmentLink(row.attachment);
|
||||
} else if (row.link) {
|
||||
var html = `<span class='fas fa-link'></span> ${row.link}`;
|
||||
let html = makeIcon('fa-link') + ` ${row.link}`;
|
||||
return renderLink(html, row.link);
|
||||
} else {
|
||||
return '-';
|
||||
@ -327,13 +327,10 @@ function loadAttachmentTable(url, options) {
|
||||
{
|
||||
field: 'actions',
|
||||
formatter: function(value, row) {
|
||||
var html = '';
|
||||
|
||||
html = `<div class='btn-group float-right' role='group'>`;
|
||||
let buttons = '';
|
||||
|
||||
if (permissions.change) {
|
||||
html += makeIconButton(
|
||||
'fa-edit icon-blue',
|
||||
buttons += makeEditButton(
|
||||
'button-attachment-edit',
|
||||
row.pk,
|
||||
'{% trans "Edit attachment" %}',
|
||||
@ -341,19 +338,30 @@ function loadAttachmentTable(url, options) {
|
||||
}
|
||||
|
||||
if (permissions.delete) {
|
||||
html += makeIconButton(
|
||||
'fa-trash-alt icon-red',
|
||||
buttons += makeDeleteButton(
|
||||
'button-attachment-delete',
|
||||
row.pk,
|
||||
'{% trans "Delete attachment" %}',
|
||||
);
|
||||
}
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
return wrapButtons(buttons);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Enable drag-and-drop functionality
|
||||
enableDragAndDrop(
|
||||
'#attachment-dropzone',
|
||||
url,
|
||||
{
|
||||
data: options.filters,
|
||||
label: 'attachment',
|
||||
method: 'POST',
|
||||
success: function() {
|
||||
reloadBootstrapTable('#attachment-table');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
/* globals
|
||||
imageHoverIcon,
|
||||
inventreePut,
|
||||
makeIconButton,
|
||||
modalEnable,
|
||||
modalSetContent,
|
||||
modalSetTitle,
|
||||
@ -43,11 +42,11 @@ function makeBarcodeInput(placeholderText='', hintText='') {
|
||||
<div class='controls'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-text'>
|
||||
<span class='fas fa-qrcode'></span>
|
||||
${makeIcon('fa-qrcode')}
|
||||
</span>
|
||||
<input id='barcode' class='textinput textInput form-control' type='text' name='barcode' placeholder='${placeholderText}'>
|
||||
<button title='{% trans "Scan barcode using connected webcam" %}' id='barcode_scan_btn' type='button' class='btn btn-secondary' onclick='onBarcodeScanClicked()' style='display: none;'>
|
||||
<span class='fas fa-camera'></span>
|
||||
${makeIcon('fa-camera')}
|
||||
</button>
|
||||
</div>
|
||||
<div id='hint_barcode_data' class='help-block'>${hintText}</div>
|
||||
@ -132,7 +131,7 @@ function makeNotesField(options={}) {
|
||||
<div class='controls'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-text'>
|
||||
<span class='fas fa-sticky-note'></span>
|
||||
${makeIcon('fa-sticky-note')}
|
||||
</span>
|
||||
<input id='notes' class='textinput textInput form-control' type='text' name='notes' placeholder='${placeholder}'>
|
||||
</div>
|
||||
@ -149,7 +148,7 @@ function postBarcodeData(barcode_data, options={}) {
|
||||
|
||||
var modal = options.modal || '#modal-form';
|
||||
|
||||
var url = options.url || '/api/barcode/';
|
||||
var url = options.url || '{% url "api-barcode-scan" %}';
|
||||
|
||||
var data = options.data || {};
|
||||
|
||||
@ -462,7 +461,7 @@ function unlinkBarcode(data, options={}) {
|
||||
accept_text: '{% trans "Unlink" %}',
|
||||
accept: function() {
|
||||
inventreePut(
|
||||
'/api/barcode/unlink/',
|
||||
'{% url "api-barcode-unlink" %}',
|
||||
data,
|
||||
{
|
||||
method: 'POST',
|
||||
@ -521,7 +520,7 @@ function barcodeCheckInStockItems(location_id, options={}) {
|
||||
<td>${imageHoverIcon(item.part_detail.thumbnail)} ${item.part_detail.name}</td>
|
||||
<td>${location_info}</td>
|
||||
<td>${item.quantity}</td>
|
||||
<td>${makeIconButton('fa-times-circle icon-red', 'button-item-remove', item.pk, '{% trans "Remove stock item" %}')}</td>
|
||||
<td>${makeRemoveButton('button-item-remove', item.pk, '{% trans "Remove stock item" %}')}</td>
|
||||
</tr>`;
|
||||
});
|
||||
|
||||
@ -691,7 +690,7 @@ function barcodeCheckInStockLocations(location_id, options={}) {
|
||||
if ('stocklocation' in response) {
|
||||
var pk = response.stocklocation.pk;
|
||||
|
||||
var url = `/api/stock/location/${pk}/`;
|
||||
var url = `{% url "api-location-list" %}${pk}/`;
|
||||
|
||||
// Move the scanned location into *this* location
|
||||
inventreePut(
|
||||
@ -812,7 +811,7 @@ function scanItemsIntoLocation(item_list, options={}) {
|
||||
|
||||
var pk = response.stocklocation.pk;
|
||||
|
||||
inventreeGet(`/api/stock/location/${pk}/`, {}, {
|
||||
inventreeGet(`{% url "api-location-list" %}${pk}/`, {}, {
|
||||
success: function(response) {
|
||||
|
||||
stock_location = response;
|
||||
|
@ -96,12 +96,12 @@ function constructBomUploadTable(data, options={}) {
|
||||
var optional = constructRowField('optional');
|
||||
var note = constructRowField('note');
|
||||
|
||||
var buttons = `<div class='btn-group float-right' role='group'>`;
|
||||
let buttons = '';
|
||||
|
||||
buttons += makeIconButton('fa-info-circle', 'button-row-data', idx, '{% trans "Display row data" %}');
|
||||
buttons += makeIconButton('fa-times icon-red', 'button-row-remove', idx, '{% trans "Remove row" %}');
|
||||
buttons += makeInfoButton('button-row-data', idx, '{% trans "Display row data" %}');
|
||||
buttons += makeRemoveButton('button-row-remove', idx, '{% trans "Remove row" %}');
|
||||
|
||||
buttons += `</div>`;
|
||||
buttons = wrapButtons(buttons);
|
||||
|
||||
var html = `
|
||||
<tr id='items_${idx}' class='bom-import-row' idx='${idx}'>
|
||||
@ -557,7 +557,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
|
||||
var buttons = '';
|
||||
|
||||
buttons += makeIconButton('fa-times icon-red', 'button-row-remove', pk, '{% trans "Remove substitute part" %}');
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% trans "Remove substitute part" %}');
|
||||
|
||||
// Render a single row
|
||||
var html = `
|
||||
@ -626,7 +626,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
</div>
|
||||
`;
|
||||
|
||||
constructForm(`/api/bom/substitute/${pk}/`, {
|
||||
constructForm(`{% url "api-bom-substitute-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Remove Substitute Part" %}',
|
||||
preFormContent: pre,
|
||||
@ -785,9 +785,7 @@ function loadBomTable(table, options={}) {
|
||||
filters = loadTableFilters('bom');
|
||||
}
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
Object.assign(filters, params);
|
||||
|
||||
setupFilterList('bom', $(table));
|
||||
|
||||
@ -1142,7 +1140,7 @@ function loadBomTable(table, options={}) {
|
||||
}
|
||||
|
||||
if (available_stock <= 0) {
|
||||
text += `<span class='fas fa-times-circle icon-red float-right' title='{% trans "No Stock Available" %}'></span>`;
|
||||
text += makeIconBadge('fa-times-circle icon-red', '{% trans "No Stock Available" %}');
|
||||
} else {
|
||||
var extra = '';
|
||||
|
||||
@ -1160,7 +1158,10 @@ function loadBomTable(table, options={}) {
|
||||
}
|
||||
|
||||
if (row.on_order && row.on_order > 0) {
|
||||
text += `<span class='fas fa-shopping-cart float-right' title='{% trans "On Order" %}: ${row.on_order}'></span>`;
|
||||
text += makeIconBadge(
|
||||
'fa-shopping-cart',
|
||||
`{% trans "On Order" %}: ${row.on_order}`,
|
||||
);
|
||||
}
|
||||
|
||||
return renderLink(text, url);
|
||||
@ -1242,11 +1243,10 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
var bSubs = makeIconButton('fa-exchange-alt icon-blue', 'bom-substitutes-button', row.pk, '{% trans "Edit substitute parts" %}');
|
||||
|
||||
var bEdit = makeIconButton('fa-edit icon-blue', 'bom-edit-button', row.pk, '{% trans "Edit BOM Item" %}');
|
||||
var bEdit = makeEditButton('bom-edit-button', row.pk, '{% trans "Edit BOM Item" %}');
|
||||
|
||||
var bDelt = makeIconButton('fa-trash-alt icon-red', 'bom-delete-button', row.pk, '{% trans "Delete BOM Item" %}');
|
||||
var bDelt = makeDeleteButton('bom-delete-button', row.pk, '{% trans "Delete BOM Item" %}');
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group' style='min-width: 100px;'>`;
|
||||
|
||||
if (!row.validated) {
|
||||
html += bValidate;
|
||||
@ -1254,13 +1254,13 @@ function loadBomTable(table, options={}) {
|
||||
html += bValid;
|
||||
}
|
||||
|
||||
html += bEdit;
|
||||
html += bSubs;
|
||||
html += bDelt;
|
||||
var buttons = '';
|
||||
buttons += bEdit;
|
||||
buttons += bSubs;
|
||||
buttons += bDelt;
|
||||
|
||||
html += `</div>`;
|
||||
return wrapButtons(buttons);
|
||||
|
||||
return html;
|
||||
} else {
|
||||
// Return a link to the external BOM
|
||||
|
||||
@ -1273,7 +1273,7 @@ function loadBomTable(table, options={}) {
|
||||
footerFormatter: function(data) {
|
||||
return `
|
||||
<button class='btn btn-success float-right' type='button' title='{% trans "Add BOM Item" %}' id='bom-item-new-footer'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "Add BOM Item" %}
|
||||
${makeIcon('fa-plus-circle')} {% trans "Add BOM Item" %}
|
||||
</button>
|
||||
`;
|
||||
}
|
||||
@ -1436,7 +1436,7 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
var fields = bomItemFields();
|
||||
|
||||
constructForm(`/api/bom/${pk}/`, {
|
||||
constructForm(`{% url "api-bom-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit BOM Item" %}',
|
||||
focus: 'sub_part',
|
||||
@ -1508,15 +1508,7 @@ function loadUsedInTable(table, part_id, options={}) {
|
||||
params.part_detail = true;
|
||||
params.sub_part_detail = true;
|
||||
|
||||
var filters = {};
|
||||
|
||||
if (!options.disableFilters) {
|
||||
filters = loadTableFilters('usedin');
|
||||
}
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
var filters = loadTableFilters('usedin', params);
|
||||
|
||||
setupFilterList('usedin', $(table), options.filterTarget || '#filter-list-usedin');
|
||||
|
||||
|
@ -90,7 +90,7 @@ function editBuildOrder(pk) {
|
||||
|
||||
var fields = buildFormFields();
|
||||
|
||||
constructForm(`/api/build/${pk}/`, {
|
||||
constructForm(`{% url "api-build-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
reload: true,
|
||||
title: '{% trans "Edit Build Order" %}',
|
||||
@ -147,7 +147,7 @@ function newBuildOrder(options={}) {
|
||||
*/
|
||||
function duplicateBuildOrder(build_id, options={}) {
|
||||
|
||||
inventreeGet(`/api/build/${build_id}/`, {}, {
|
||||
inventreeGet(`{% url "api-build-list" %}${build_id}/`, {}, {
|
||||
success: function(data) {
|
||||
// Clear out data we do not want to be duplicated
|
||||
delete data['pk'];
|
||||
@ -166,7 +166,7 @@ function duplicateBuildOrder(build_id, options={}) {
|
||||
function cancelBuildOrder(build_id, options={}) {
|
||||
|
||||
constructForm(
|
||||
`/api/build/${build_id}/cancel/`,
|
||||
`{% url "api-build-list" %}${build_id}/cancel/`,
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Cancel Build Order" %}',
|
||||
@ -208,7 +208,7 @@ function cancelBuildOrder(build_id, options={}) {
|
||||
/* Construct a form to "complete" (finish) a build order */
|
||||
function completeBuildOrder(build_id, options={}) {
|
||||
|
||||
constructForm(`/api/build/${build_id}/finish/`, {
|
||||
constructForm(`{% url "api-build-list" %}${build_id}/finish/`, {
|
||||
fieldsFunction: function(opts) {
|
||||
var ctx = opts.context || {};
|
||||
|
||||
@ -287,7 +287,7 @@ function createBuildOutput(build_id, options) {
|
||||
|
||||
// Request build order information from the server
|
||||
inventreeGet(
|
||||
`/api/build/${build_id}/`,
|
||||
`{% url "api-build-list" %}${build_id}/`,
|
||||
{},
|
||||
{
|
||||
success: function(build) {
|
||||
@ -312,7 +312,7 @@ function createBuildOutput(build_id, options) {
|
||||
};
|
||||
|
||||
// Work out the next available serial numbers
|
||||
inventreeGet(`/api/part/${build.part}/serial-numbers/`, {}, {
|
||||
inventreeGet(`{% url "api-part-list" %}${build.part}/serial-numbers/`, {}, {
|
||||
success: function(data) {
|
||||
if (data.next) {
|
||||
fields.serial_numbers.placeholder = `{% trans "Next available serial number" %}: ${data.next}`;
|
||||
@ -341,7 +341,7 @@ function createBuildOutput(build_id, options) {
|
||||
`;
|
||||
}
|
||||
|
||||
constructForm(`/api/build/${build_id}/create-output/`, {
|
||||
constructForm(`{% url "api-build-list" %}${build_id}/create-output/`, {
|
||||
method: 'POST',
|
||||
title: '{% trans "Create Build Output" %}',
|
||||
confirm: true,
|
||||
@ -364,7 +364,7 @@ function createBuildOutput(build_id, options) {
|
||||
*/
|
||||
function makeBuildOutputButtons(output_id, build_info, options={}) {
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
var html = '';
|
||||
|
||||
// Tracked parts? Must be individually allocated
|
||||
if (options.has_bom_items) {
|
||||
@ -398,17 +398,13 @@ function makeBuildOutputButtons(output_id, build_info, options={}) {
|
||||
);
|
||||
|
||||
// Add a button to "delete" this build output
|
||||
html += makeIconButton(
|
||||
'fa-trash-alt icon-red',
|
||||
html += makeDeleteButton(
|
||||
'button-output-delete',
|
||||
output_id,
|
||||
'{% trans "Delete build output" %}',
|
||||
);
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
|
||||
return wrapButtons(html);
|
||||
}
|
||||
|
||||
|
||||
@ -421,7 +417,7 @@ function makeBuildOutputButtons(output_id, build_info, options={}) {
|
||||
*/
|
||||
function unallocateStock(build_id, options={}) {
|
||||
|
||||
var url = `/api/build/${build_id}/unallocate/`;
|
||||
var url = `{% url "api-build-list" %}${build_id}/unallocate/`;
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
@ -486,7 +482,7 @@ function completeBuildOutputs(build_id, outputs, options={}) {
|
||||
|
||||
var buttons = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
buttons += makeIconButton('fa-times icon-red', 'button-row-remove', pk, '{% trans "Remove row" %}');
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% trans "Remove row" %}');
|
||||
|
||||
buttons += '</div>';
|
||||
|
||||
@ -529,7 +525,7 @@ function completeBuildOutputs(build_id, outputs, options={}) {
|
||||
</tbody>
|
||||
</table>`;
|
||||
|
||||
constructForm(`/api/build/${build_id}/complete/`, {
|
||||
constructForm(`{% url "api-build-list" %}${build_id}/complete/`, {
|
||||
method: 'POST',
|
||||
preFormContent: html,
|
||||
fields: {
|
||||
@ -647,7 +643,7 @@ function deleteBuildOutputs(build_id, outputs, options={}) {
|
||||
|
||||
var buttons = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
buttons += makeIconButton('fa-times icon-red', 'button-row-remove', pk, '{% trans "Remove row" %}');
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% trans "Remove row" %}');
|
||||
|
||||
buttons += '</div>';
|
||||
|
||||
@ -690,7 +686,7 @@ function deleteBuildOutputs(build_id, outputs, options={}) {
|
||||
</tbody>
|
||||
</table>`;
|
||||
|
||||
constructForm(`/api/build/${build_id}/delete-outputs/`, {
|
||||
constructForm(`{% url "api-build-list" %}${build_id}/delete-outputs/`, {
|
||||
method: 'POST',
|
||||
preFormContent: html,
|
||||
fields: {},
|
||||
@ -768,11 +764,7 @@ function loadBuildOrderAllocationTable(table, options={}) {
|
||||
options.params['location_detail'] = true;
|
||||
options.params['stock_detail'] = true;
|
||||
|
||||
var filters = loadTableFilters('buildorderallocation');
|
||||
|
||||
for (var key in options.params) {
|
||||
filters[key] = options.params[key];
|
||||
}
|
||||
var filters = loadTableFilters('buildorderallocation', options.params);
|
||||
|
||||
setupFilterList('buildorderallocation', $(table));
|
||||
|
||||
@ -893,7 +885,12 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
setupFilterList('builditems', $(table), options.filterTarget || '#filter-list-incompletebuilditems');
|
||||
setupFilterList('builditems', $(table), options.filterTarget || '#filter-list-incompletebuilditems', {
|
||||
labels: {
|
||||
url: '{% url "api-stockitem-label-list" %}',
|
||||
key: 'item',
|
||||
}
|
||||
});
|
||||
|
||||
function setupBuildOutputButtonCallbacks() {
|
||||
|
||||
@ -1407,19 +1404,6 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
);
|
||||
});
|
||||
|
||||
// Print stock item labels
|
||||
$('#incomplete-output-print-label').click(function() {
|
||||
var outputs = getTableData(table);
|
||||
|
||||
var stock_id_values = [];
|
||||
|
||||
outputs.forEach(function(output) {
|
||||
stock_id_values.push(output.pk);
|
||||
});
|
||||
|
||||
printStockItemLabels(stock_id_values);
|
||||
});
|
||||
|
||||
$('#outputs-expand').click(function() {
|
||||
$(table).bootstrapTable('expandAllRows');
|
||||
});
|
||||
@ -1482,13 +1466,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
}
|
||||
|
||||
// Filters
|
||||
var filters = loadTableFilters('builditems');
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
let filters = loadTableFilters('builditems', options.params);
|
||||
|
||||
setupFilterList('builditems', $(table), options.filterTarget);
|
||||
|
||||
@ -1703,6 +1681,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
name: 'build-allocation',
|
||||
uniqueId: 'sub_part',
|
||||
search: options.search || false,
|
||||
queryParams: filters,
|
||||
original: options.params,
|
||||
onPostBody: function(data) {
|
||||
// Setup button callbacks
|
||||
setupCallbacks();
|
||||
@ -1796,15 +1776,13 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
|
||||
var pk = row.pk;
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
var html = '';
|
||||
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-allocation-edit', pk, '{% trans "Edit stock allocation" %}');
|
||||
html += makeEditButton('button-allocation-edit', pk, '{% trans "Edit stock allocation" %}');
|
||||
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-allocation-delete', pk, '{% trans "Delete stock allocation" %}');
|
||||
html += makeDeleteButton('button-allocation-delete', pk, '{% trans "Delete stock allocation" %}');
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
}
|
||||
}
|
||||
]
|
||||
@ -1814,7 +1792,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
subTable.find('.button-allocation-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/build/item/${pk}/`, {
|
||||
constructForm(`{% url "api-build-item-list" %}${pk}/`, {
|
||||
fields: {
|
||||
quantity: {},
|
||||
},
|
||||
@ -1826,7 +1804,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
subTable.find('.button-allocation-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/build/item/${pk}/`, {
|
||||
constructForm(`{% url "api-build-item-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Remove Allocation" %}',
|
||||
onSuccess: reloadAllocationData,
|
||||
@ -1935,9 +1913,9 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
icons += `<span class='fas fa-info-circle icon-blue float-right' title='{% trans "Consumable item" %}'></span>`;
|
||||
} else {
|
||||
if (available_stock < (required - allocated)) {
|
||||
icons += `<span class='fas fa-times-circle icon-red float-right' title='{% trans "Insufficient stock available" %}'></span>`;
|
||||
icons += makeIconBadge('fa-times-circle icon-red', '{% trans "Insufficient stock available" %}');
|
||||
} else {
|
||||
icons += `<span class='fas fa-check-circle icon-green float-right' title='{% trans "Sufficient stock available" %}'></span>`;
|
||||
icons += makeIconBadge('fa-check-circle icon-green', '{% trans "Sufficient stock available" %}');
|
||||
}
|
||||
|
||||
if (available_stock <= 0) {
|
||||
@ -1953,13 +1931,15 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
}
|
||||
|
||||
if (extra) {
|
||||
icons += `<span title='${extra}' class='fas fa-info-circle float-right icon-blue'></span>`;
|
||||
icons += makeInfoButton('fa-info-circle icon-blue', extra);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (row.on_order && row.on_order > 0) {
|
||||
icons += `<span class='fas fa-shopping-cart float-right' title='{% trans "On Order" %}: ${row.on_order}'></span>`;
|
||||
makeIconBadge('fa-shopping-cart', '{% trans "On Order" %}', {
|
||||
content: row.on_order,
|
||||
});
|
||||
}
|
||||
|
||||
return renderLink(text, url) + icons;
|
||||
@ -2027,7 +2007,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
}
|
||||
|
||||
// Generate action buttons for this build output
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
let html = '';
|
||||
|
||||
if (allocatedQuantity(row) < requiredQuantity(row)) {
|
||||
if (row.sub_part_detail.assembly) {
|
||||
@ -2041,17 +2021,16 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
html += makeIconButton('fa-sign-in-alt icon-green', 'button-add', row.sub_part, '{% trans "Allocate stock" %}');
|
||||
}
|
||||
|
||||
html += makeIconButton(
|
||||
'fa-minus-circle icon-red', 'button-unallocate', row.sub_part,
|
||||
html += makeRemoveButton(
|
||||
'button-unallocate',
|
||||
row.sub_part,
|
||||
'{% trans "Unallocate stock" %}',
|
||||
{
|
||||
disabled: allocatedQuantity(row) == 0,
|
||||
}
|
||||
);
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
}
|
||||
},
|
||||
]
|
||||
@ -2093,7 +2072,7 @@ function allocateStockToBuild(build_id, part_id, bom_items, options={}) {
|
||||
|
||||
if (output_id) {
|
||||
// Request information on the particular build output (stock item)
|
||||
inventreeGet(`/api/stock/${output_id}/`, {}, {
|
||||
inventreeGet(`{% url "api-stock-list" %}${output_id}/`, {}, {
|
||||
success: function(output) {
|
||||
if (output.quantity == 1 && output.serial != null) {
|
||||
auto_fill_filters.serial = output.serial;
|
||||
@ -2112,8 +2091,7 @@ function allocateStockToBuild(build_id, part_id, bom_items, options={}) {
|
||||
|
||||
var delete_button = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
delete_button += makeIconButton(
|
||||
'fa-times icon-red',
|
||||
delete_button += makeRemoveButton(
|
||||
'button-row-remove',
|
||||
pk,
|
||||
'{% trans "Remove row" %}',
|
||||
@ -2245,7 +2223,7 @@ function allocateStockToBuild(build_id, part_id, bom_items, options={}) {
|
||||
</table>
|
||||
`;
|
||||
|
||||
constructForm(`/api/build/${build_id}/allocate/`, {
|
||||
constructForm(`{% url "api-build-list" %}${build_id}/allocate/`, {
|
||||
method: 'POST',
|
||||
fields: {},
|
||||
preFormContent: html,
|
||||
@ -2459,7 +2437,7 @@ function autoAllocateStockToBuild(build_id, bom_items=[], options={}) {
|
||||
},
|
||||
};
|
||||
|
||||
constructForm(`/api/build/${build_id}/auto-allocate/`, {
|
||||
constructForm(`{% url "api-build-list" %}${build_id}/auto-allocate/`, {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Allocate Stock Items" %}',
|
||||
@ -2484,21 +2462,19 @@ function loadBuildTable(table, options) {
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
var filters = {};
|
||||
|
||||
params['part_detail'] = true;
|
||||
|
||||
if (!options.disableFilters) {
|
||||
filters = loadTableFilters('build');
|
||||
}
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
var filters = loadTableFilters('build', params);
|
||||
|
||||
var filterTarget = options.filterTarget || null;
|
||||
|
||||
setupFilterList('build', table, filterTarget, {download: true});
|
||||
setupFilterList('build', table, filterTarget, {
|
||||
download: true,
|
||||
report: {
|
||||
url: '{% url "api-build-report-list" %}',
|
||||
key: 'build',
|
||||
}
|
||||
});
|
||||
|
||||
// Which display mode to use for the build table?
|
||||
var display_mode = inventreeLoad('build-table-display-mode', 'list');
|
||||
|
@ -4,23 +4,26 @@
|
||||
constructForm,
|
||||
imageHoverIcon,
|
||||
loadTableFilters,
|
||||
makeIconButton,
|
||||
renderLink,
|
||||
setupFilterList,
|
||||
*/
|
||||
|
||||
/* exported
|
||||
createCompany,
|
||||
createContact,
|
||||
createManufacturerPart,
|
||||
createSupplierPart,
|
||||
createSupplierPartPriceBreak,
|
||||
deleteContacts,
|
||||
deleteManufacturerParts,
|
||||
deleteManufacturerPartParameters,
|
||||
deleteSupplierParts,
|
||||
duplicateSupplierPart,
|
||||
editCompany,
|
||||
editContact,
|
||||
editSupplierPartPriceBreak,
|
||||
loadCompanyTable,
|
||||
loadContactTable,
|
||||
loadManufacturerPartTable,
|
||||
loadManufacturerPartParameterTable,
|
||||
loadSupplierPartTable,
|
||||
@ -197,7 +200,7 @@ function createSupplierPart(options={}) {
|
||||
var header = '';
|
||||
if (options.part) {
|
||||
var part_model = {};
|
||||
inventreeGet(`/api/part/${options.part}/.*`, {}, {
|
||||
inventreeGet(`{% url "api-part-list" %}${options.part}/.*`, {}, {
|
||||
async: false,
|
||||
success: function(response) {
|
||||
part_model = response;
|
||||
@ -226,7 +229,7 @@ function duplicateSupplierPart(part, options={}) {
|
||||
var fields = options.fields || supplierPartFields();
|
||||
|
||||
// Retrieve information for the supplied part
|
||||
inventreeGet(`/api/company/part/${part}/`, {}, {
|
||||
inventreeGet(`{% url "api-supplier-part-list" %}${part}/`, {}, {
|
||||
success: function(data) {
|
||||
|
||||
// Remove fields which we do not want to duplicate
|
||||
@ -234,7 +237,7 @@ function duplicateSupplierPart(part, options={}) {
|
||||
delete data['available'];
|
||||
delete data['availability_updated'];
|
||||
|
||||
constructForm(`/api/company/part/`, {
|
||||
constructForm('{% url "api-supplier-part-list" %}', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Duplicate Supplier Part" %}',
|
||||
@ -260,7 +263,7 @@ function editSupplierPart(part, options={}) {
|
||||
fields.part.hidden = true;
|
||||
}
|
||||
|
||||
constructForm(`/api/company/part/${part}/`, {
|
||||
constructForm(`{% url "api-supplier-part-list" %}${part}/`, {
|
||||
fields: fields,
|
||||
title: options.title || '{% trans "Edit Supplier Part" %}',
|
||||
onSuccess: options.onSuccess
|
||||
@ -443,24 +446,18 @@ function createCompany(options={}) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load company listing data into specified table.
|
||||
*
|
||||
* Args:
|
||||
* - table: Table element on the page
|
||||
* - url: Base URL for the API query
|
||||
* - options: table options.
|
||||
*/
|
||||
function loadCompanyTable(table, url, options={}) {
|
||||
/*
|
||||
* Load company listing data into specified table.
|
||||
*
|
||||
* Args:
|
||||
* - table: Table element on the page
|
||||
* - url: Base URL for the API query
|
||||
* - options: table options.
|
||||
*/
|
||||
|
||||
// Query parameters
|
||||
var params = options.params || {};
|
||||
|
||||
var filters = loadTableFilters('company');
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
let params = options.params || {};
|
||||
let filters = loadTableFilters('company', params);
|
||||
|
||||
setupFilterList('company', $(table));
|
||||
|
||||
@ -547,6 +544,230 @@ function loadCompanyTable(table, url, options={}) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a set of form fields for the Contact model
|
||||
*/
|
||||
function contactFields(options={}) {
|
||||
|
||||
let fields = {
|
||||
company: {
|
||||
icon: 'fa-building',
|
||||
},
|
||||
name: {
|
||||
icon: 'fa-user',
|
||||
},
|
||||
phone: {
|
||||
icon: 'fa-phone'
|
||||
},
|
||||
email: {
|
||||
icon: 'fa-at',
|
||||
},
|
||||
role: {
|
||||
icon: 'fa-user-tag',
|
||||
},
|
||||
};
|
||||
|
||||
if (options.company) {
|
||||
fields.company.value = options.company;
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launches a form to create a new Contact
|
||||
*/
|
||||
function createContact(options={}) {
|
||||
let fields = options.fields || contactFields(options);
|
||||
|
||||
constructForm('{% url "api-contact-list" %}', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Create New Contact" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launches a form to edit an existing Contact
|
||||
*/
|
||||
function editContact(pk, options={}) {
|
||||
let fields = options.fields || contactFields(options);
|
||||
|
||||
constructForm(`{% url "api-contact-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit Contact" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launches a form to delete one (or more) contacts
|
||||
*/
|
||||
function deleteContacts(contacts, options={}) {
|
||||
|
||||
if (contacts.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
function renderContact(contact) {
|
||||
return `
|
||||
<tr>
|
||||
<td>${contact.name}</td>
|
||||
<td>${contact.email}</td>
|
||||
<td>${contact.role}</td>
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
let rows = '';
|
||||
let ids = [];
|
||||
|
||||
contacts.forEach(function(contact) {
|
||||
rows += renderContact(contact);
|
||||
ids.push(contact.pk);
|
||||
});
|
||||
|
||||
let html = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
{% trans "All selected contacts will be deleted" %}
|
||||
</div>
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "Email" %}</th>
|
||||
<th>{% trans "Role" %}</th>
|
||||
</tr>
|
||||
${rows}
|
||||
</table>`;
|
||||
|
||||
constructForm('{% url "api-contact-list" %}', {
|
||||
method: 'DELETE',
|
||||
multi_delete: true,
|
||||
title: '{% trans "Delete Contacts" %}',
|
||||
preFormContent: html,
|
||||
form_data: {
|
||||
items: ids,
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load table listing company contacts
|
||||
*/
|
||||
function loadContactTable(table, options={}) {
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
var filters = loadTableFilters('contact', params);
|
||||
|
||||
setupFilterList('contact', $(table), '#filter-list-contacts');
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: '{% url "api-contact-list" %}',
|
||||
queryParams: filters,
|
||||
original: params,
|
||||
idField: 'pk',
|
||||
uniqueId: 'pk',
|
||||
sidePagination: 'server',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No contacts found" %}';
|
||||
},
|
||||
showColumns: true,
|
||||
name: 'contacts',
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Name" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'phone',
|
||||
title: '{% trans "Phone Number" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
field: 'email',
|
||||
title: '{% trans "Email Address" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
field: 'role',
|
||||
title: '{% trans "Role" %}',
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
title: '',
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
visible: options.allow_edit || options.allow_delete,
|
||||
formatter: function(value, row) {
|
||||
var pk = row.pk;
|
||||
|
||||
let html = '';
|
||||
|
||||
if (options.allow_edit) {
|
||||
html += makeEditButton('btn-contact-edit', pk, '{% trans "Edit Contact" %}');
|
||||
}
|
||||
|
||||
if (options.allow_delete) {
|
||||
html += makeDeleteButton('btn-contact-delete', pk, '{% trans "Delete Contact" %}');
|
||||
}
|
||||
|
||||
return wrapButtons(html);
|
||||
}
|
||||
}
|
||||
],
|
||||
onPostBody: function() {
|
||||
// Edit button callback
|
||||
if (options.allow_edit) {
|
||||
$(table).find('.btn-contact-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
editContact(pk, {
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Delete button callback
|
||||
if (options.allow_delete) {
|
||||
$(table).find('.btn-contact-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
var row = $(table).bootstrapTable('getRowByUniqueId', pk);
|
||||
|
||||
if (row && row.pk) {
|
||||
|
||||
deleteContacts([row], {
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* Delete one or more ManufacturerPart objects from the database.
|
||||
* - User will be provided with a modal form, showing all the parts to be deleted.
|
||||
* - Delete operations are performed sequentialy, not simultaneously
|
||||
@ -653,21 +874,16 @@ function deleteManufacturerPartParameters(selections, options={}) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load manufacturer part table
|
||||
*/
|
||||
function loadManufacturerPartTable(table, url, options) {
|
||||
/*
|
||||
* Load manufacturer part table
|
||||
*
|
||||
*/
|
||||
|
||||
// Query parameters
|
||||
var params = options.params || {};
|
||||
|
||||
// Load filters
|
||||
var filters = loadTableFilters('manufacturer-part');
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
var filters = loadTableFilters('manufacturer-part', params);
|
||||
|
||||
var filterTarget = options.filterTarget || '#filter-list-manufacturer-part';
|
||||
|
||||
@ -703,11 +919,11 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value, url);
|
||||
|
||||
if (row.part_detail.is_template) {
|
||||
html += `<span class='fas fa-clone float-right' title='{% trans "Template part" %}'></span>`;
|
||||
html += makeIconBadge('fa-clone', '{% trans "Template part" %}');
|
||||
}
|
||||
|
||||
if (row.part_detail.assembly) {
|
||||
html += `<span class='fas fa-tools float-right' title='{% trans "Assembled part" %}'></span>`;
|
||||
html += makeIconBadge('fa-tools', '{% trans "Assembled part" %}');
|
||||
}
|
||||
|
||||
if (!row.part_detail.active) {
|
||||
@ -764,16 +980,13 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
var pk = row.pk;
|
||||
let pk = row.pk;
|
||||
let html = '';
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
html += makeEditButton('button-manufacturer-part-edit', pk, '{% trans "Edit manufacturer part" %}');
|
||||
html += makeDeleteButton('button-manufacturer-part-delete', pk, '{% trans "Delete manufacturer part" %}');
|
||||
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-manufacturer-part-edit', pk, '{% trans "Edit manufacturer part" %}');
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-manufacturer-part-delete', pk, '{% trans "Delete manufacturer part" %}');
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -810,20 +1023,15 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load table of ManufacturerPartParameter objects
|
||||
*/
|
||||
function loadManufacturerPartParameterTable(table, url, options) {
|
||||
/*
|
||||
* Load table of ManufacturerPartParameter objects
|
||||
*/
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
// Load filters
|
||||
var filters = loadTableFilters('manufacturer-part-parameters');
|
||||
|
||||
// Overwrite explicit parameters
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
var filters = loadTableFilters('manufacturer-part-parameters', params);
|
||||
|
||||
setupFilterList('manufacturer-part-parameters', $(table));
|
||||
|
||||
@ -867,17 +1075,13 @@ function loadManufacturerPartParameterTable(table, url, options) {
|
||||
switchable: false,
|
||||
sortable: false,
|
||||
formatter: function(value, row) {
|
||||
let pk = row.pk;
|
||||
let html = '';
|
||||
|
||||
var pk = row.pk;
|
||||
html += makeEditButton('button-parameter-edit', pk, '{% trans "Edit parameter" %}');
|
||||
html += makeDeleteButton('button-parameter-delete', pk, '{% trans "Delete parameter" %}');
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-parameter-edit', pk, '{% trans "Edit parameter" %}');
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-parameter-delete', pk, '{% trans "Delete parameter" %}');
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -886,27 +1090,23 @@ function loadManufacturerPartParameterTable(table, url, options) {
|
||||
$(table).find('.button-parameter-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/company/part/manufacturer/parameter/${pk}/`, {
|
||||
constructForm(`{% url "api-manufacturer-part-parameter-list" %}${pk}/`, {
|
||||
fields: {
|
||||
name: {},
|
||||
value: {},
|
||||
units: {},
|
||||
},
|
||||
title: '{% trans "Edit Parameter" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
$(table).find('.button-parameter-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/company/part/manufacturer/parameter/${pk}/`, {
|
||||
constructForm(`{% url "api-manufacturer-part-parameter-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Parameter" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -914,21 +1114,16 @@ function loadManufacturerPartParameterTable(table, url, options) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load supplier part table
|
||||
*/
|
||||
function loadSupplierPartTable(table, url, options) {
|
||||
/*
|
||||
* Load supplier part table
|
||||
*
|
||||
*/
|
||||
|
||||
// Query parameters
|
||||
var params = options.params || {};
|
||||
|
||||
// Load filters
|
||||
var filters = loadTableFilters('supplier-part');
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
var filters = loadTableFilters('supplier-part', params);
|
||||
|
||||
setupFilterList('supplier-part', $(table));
|
||||
|
||||
@ -964,11 +1159,11 @@ function loadSupplierPartTable(table, url, options) {
|
||||
var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value, url);
|
||||
|
||||
if (row.part_detail.is_template) {
|
||||
html += `<span class='fas fa-clone float-right' title='{% trans "Template part" %}'></span>`;
|
||||
html += makeIconBadge('fa-clone', '{% trans "Template part" %}');
|
||||
}
|
||||
|
||||
if (row.part_detail.assembly) {
|
||||
html += `<span class='fas fa-tools float-right' title='{% trans "Assembled part" %}'></span>`;
|
||||
html += makeIconBadge('fa-tools', '{% trans "Assembled part" %}');
|
||||
}
|
||||
|
||||
if (!row.part_detail.active) {
|
||||
@ -1088,9 +1283,13 @@ function loadSupplierPartTable(table, url, options) {
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
if (row.availability_updated) {
|
||||
var html = formatDecimal(value);
|
||||
var date = renderDate(row.availability_updated, {showTime: true});
|
||||
html += `<span class='fas fa-info-circle float-right' title='{% trans "Last Updated" %}: ${date}'></span>`;
|
||||
let html = formatDecimal(value);
|
||||
let date = renderDate(row.availability_updated, {showTime: true});
|
||||
|
||||
html += makeIconBadge(
|
||||
'fa-info-circle',
|
||||
`{% trans "Last Updated" %}: ${date}`
|
||||
);
|
||||
return html;
|
||||
} else {
|
||||
return '-';
|
||||
@ -1108,16 +1307,13 @@ function loadSupplierPartTable(table, url, options) {
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
var pk = row.pk;
|
||||
let pk = row.pk;
|
||||
let html = '';
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
html += makeEditButton('button-supplier-part-edit', pk, '{% trans "Edit supplier part" %}');
|
||||
html += makeDeleteButton('button-supplier-part-delete', pk, '{% trans "Delete supplier part" %}');
|
||||
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-supplier-part-edit', pk, '{% trans "Edit supplier part" %}');
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-supplier-part-delete', pk, '{% trans "Delete supplier part" %}');
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -1166,24 +1362,20 @@ function loadSupplierPriceBreakTable(options={}) {
|
||||
table.find('.button-price-break-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/company/price-break/${pk}/`, {
|
||||
constructForm(`{% url "api-part-supplier-price-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Price Break" %}',
|
||||
onSuccess: function() {
|
||||
table.bootstrapTable('refresh');
|
||||
},
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
|
||||
table.find('.button-price-break-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/company/price-break/${pk}/`, {
|
||||
constructForm(`{% url "api-part-supplier-price-list" %}${pk}/`, {
|
||||
fields: supplierPartPriceBreakFields(),
|
||||
title: '{% trans "Edit Price Break" %}',
|
||||
onSuccess: function() {
|
||||
table.bootstrapTable('refresh');
|
||||
}
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1231,10 +1423,12 @@ function loadSupplierPriceBreakTable(options={}) {
|
||||
formatter: function(value, row) {
|
||||
var html = renderDate(value);
|
||||
|
||||
html += `<div class='btn-group float-right' role='group'>`;
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-price-break-edit', row.pk, '{% trans "Edit price break" %}');
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-price-break-delete', row.pk, '{% trans "Delete price break" %}');
|
||||
html += `</div>`;
|
||||
let buttons = '';
|
||||
|
||||
buttons += makeEditButton('button-price-break-edit', row.pk, '{% trans "Edit price break" %}');
|
||||
buttons += makeDeleteButton('button-price-break-delete', row.pk, '{% trans "Delete price break" %}');
|
||||
|
||||
html += wrapButtons(buttons);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ function defaultFilters() {
|
||||
* @param tableKey - String key for the particular table
|
||||
* @param defaults - Default filters for this table e.g. 'cascade=1&location=5'
|
||||
*/
|
||||
function loadTableFilters(tableKey) {
|
||||
function loadTableFilters(tableKey, query={}) {
|
||||
|
||||
var lookup = 'table-filters-' + tableKey.toLowerCase();
|
||||
|
||||
@ -67,6 +67,9 @@ function loadTableFilters(tableKey) {
|
||||
}
|
||||
});
|
||||
|
||||
// Override configurable filters with hard-coded query
|
||||
Object.assign(filters, query);
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
@ -258,6 +261,18 @@ function generateFilterInput(tableKey, filterKey) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Helper function to make a 'filter' style button
|
||||
*/
|
||||
function makeFilterButton(options={}) {
|
||||
|
||||
return `
|
||||
<button id='${options.id}' title='${options.title}' class='btn btn-outline-secondary filter-button'>
|
||||
<span class='fas ${options.icon}'></span>
|
||||
</button>`;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure a filter list for a given table
|
||||
*
|
||||
@ -290,21 +305,58 @@ function setupFilterList(tableKey, table, target, options={}) {
|
||||
// One blank slate, please
|
||||
element.empty();
|
||||
|
||||
// Construct a set of buttons
|
||||
var buttons = '';
|
||||
|
||||
// Add 'print reports' button
|
||||
if (options.report && global_settings.REPORT_ENABLE) {
|
||||
buttons += makeFilterButton({
|
||||
id: `print-report-${tableKey}`,
|
||||
title: options.report.title || '{% trans "Print reports for selected items" %}',
|
||||
icon: 'fa-print',
|
||||
});
|
||||
}
|
||||
|
||||
// Add 'print labels' button
|
||||
if (options.labels && global_settings.LABEL_ENABLE) {
|
||||
buttons += makeFilterButton({
|
||||
id: `print-labels-${tableKey}`,
|
||||
title: options.labels.title || '{% trans "Print labels for selected items" %}',
|
||||
icon: 'fa-tag',
|
||||
});
|
||||
}
|
||||
|
||||
// Add download button
|
||||
if (options.download) {
|
||||
buttons += `<button id='download-${tableKey}' title='{% trans "Download data" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-download'></span></button>`;
|
||||
buttons += makeFilterButton({
|
||||
id: `download-${tableKey}`,
|
||||
title: '{% trans "Download table data" %}',
|
||||
icon: 'fa-download',
|
||||
});
|
||||
}
|
||||
|
||||
buttons += `<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-redo-alt'></span></button>`;
|
||||
buttons += makeFilterButton({
|
||||
id: `reload-${tableKey}`,
|
||||
title: '{% trans "Reload table data" %}',
|
||||
icon: 'fa-redo-alt',
|
||||
});
|
||||
|
||||
// If there are filters defined for this table, add more buttons
|
||||
if (!jQuery.isEmptyObject(getAvailableTableFilters(tableKey))) {
|
||||
buttons += `<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-filter'></span></button>`;
|
||||
|
||||
buttons += makeFilterButton({
|
||||
id: add,
|
||||
title: '{% trans "Add new filter" %}',
|
||||
icon: 'fa-filter',
|
||||
});
|
||||
|
||||
|
||||
if (Object.keys(filters).length > 0) {
|
||||
buttons += `<button id='${clear}' title='{% trans "Clear all filters" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-backspace icon-red'></span></button>`;
|
||||
buttons += makeFilterButton({
|
||||
id: clear,
|
||||
title: '{% trans "Clear all filters" %}',
|
||||
icon: 'fa-backspace icon-red',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -331,6 +383,42 @@ function setupFilterList(tableKey, table, target, options={}) {
|
||||
element.append(filter_tag);
|
||||
}
|
||||
|
||||
// Callback for printing reports
|
||||
if (options.report && global_settings.REPORT_ENABLE) {
|
||||
element.find(`#print-report-${tableKey}`).click(function() {
|
||||
let data = getTableData(table);
|
||||
let items = [];
|
||||
|
||||
data.forEach(function(row) {
|
||||
items.push(row.pk);
|
||||
});
|
||||
|
||||
printReports({
|
||||
items: items,
|
||||
url: options.report.url,
|
||||
key: options.report.key
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Callback for printing labels
|
||||
if (options.labels && global_settings.LABEL_ENABLE) {
|
||||
element.find(`#print-labels-${tableKey}`).click(function() {
|
||||
let data = getTableData(table);
|
||||
let items = [];
|
||||
|
||||
data.forEach(function(row) {
|
||||
items.push(row.pk);
|
||||
});
|
||||
|
||||
printLabels({
|
||||
items: items,
|
||||
url: options.labels.url,
|
||||
key: options.labels.key,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Callback for reloading the table
|
||||
element.find(`#reload-${tableKey}`).click(function() {
|
||||
reloadTableFilters(table);
|
||||
|
@ -308,7 +308,7 @@ function constructDeleteForm(fields, options) {
|
||||
* - confirmText: Text for confirm button (default = "Confirm")
|
||||
*
|
||||
*/
|
||||
function constructForm(url, options) {
|
||||
function constructForm(url, options={}) {
|
||||
|
||||
// An "empty" form will be defined locally
|
||||
if (url == null) {
|
||||
@ -1169,6 +1169,11 @@ function handleFormSuccess(response, options) {
|
||||
$(options.modal).modal('hide');
|
||||
}
|
||||
|
||||
// Refresh a table
|
||||
if (options.refreshTable) {
|
||||
reloadBootstrapTable(options.refreshTable);
|
||||
}
|
||||
|
||||
if (options.onSuccess) {
|
||||
// Callback function
|
||||
options.onSuccess(response, options);
|
||||
|
@ -6,9 +6,14 @@
|
||||
editButton,
|
||||
formatDecimal,
|
||||
imageHoverIcon,
|
||||
makeCopyButton,
|
||||
makeDeleteButton,
|
||||
makeEditButton,
|
||||
makeIconBadge,
|
||||
makeIconButton,
|
||||
makeInfoButton,
|
||||
makeProgressBar,
|
||||
makeRemoveButton,
|
||||
renderLink,
|
||||
sanitizeInputString,
|
||||
select2Thumbnail,
|
||||
@ -17,8 +22,14 @@
|
||||
thumbnailImage
|
||||
yesNoLabel,
|
||||
withTitle,
|
||||
wrapButtons,
|
||||
*/
|
||||
|
||||
/* exported
|
||||
makeIcon,
|
||||
*/
|
||||
|
||||
|
||||
function yesNoLabel(value, options={}) {
|
||||
var text = '';
|
||||
var color = '';
|
||||
@ -147,17 +158,47 @@ function select2Thumbnail(image) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a simple FontAwesome icon span
|
||||
*/
|
||||
function makeIcon(icon, title='', options={}) {
|
||||
|
||||
let classes = options.classes || 'fas';
|
||||
|
||||
return `<span class='${classes} ${icon}' title='${title}'></span>`;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct an 'icon badge' which floats to the right of an object
|
||||
*/
|
||||
function makeIconBadge(icon, title) {
|
||||
function makeIconBadge(icon, title='', options={}) {
|
||||
|
||||
var html = `<span class='icon-badge fas ${icon} float-right' title='${title}'></span>`;
|
||||
let content = options.content || '';
|
||||
|
||||
let html = `
|
||||
<span class='icon-badge fas ${icon} float-right' title='${title}'>
|
||||
${content}
|
||||
</span>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Wrap list of buttons in a button group <div>
|
||||
*/
|
||||
function wrapButtons(buttons) {
|
||||
|
||||
if (!buttons) {
|
||||
// Return empty element if no buttons are provided
|
||||
return '';
|
||||
}
|
||||
|
||||
return `<div class='btn-group float-right' role='group'>${buttons}</div>`;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct an 'icon button' using the fontawesome set
|
||||
*/
|
||||
@ -187,6 +228,46 @@ function makeIconButton(icon, cls, pk, title, options={}) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Helper function for making a common 'info' button
|
||||
*/
|
||||
function makeInfoButton(cls, pk, title, options={}) {
|
||||
return makeIconButton('fa-info-circle', cls, pk, title, options);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Helper function for making a common 'edit' button
|
||||
*/
|
||||
function makeEditButton(cls, pk, title, options={}) {
|
||||
return makeIconButton('fa-edit icon-blue', cls, pk, title, options);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Helper function for making a common 'copy' button
|
||||
*/
|
||||
function makeCopyButton(cls, pk, title, options={}) {
|
||||
return makeIconButton('fa-clone', cls, pk, title, options);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Helper function for making a common 'delete' button
|
||||
*/
|
||||
function makeDeleteButton(cls, pk, title, options={}) {
|
||||
return makeIconButton('fa-trash-alt icon-red', cls, pk, title, options);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Helper function for making a common 'remove' button
|
||||
*/
|
||||
function makeRemoveButton(cls, pk, title, options={}) {
|
||||
return makeIconButton('fa-times-circle icon-red', cls, pk, title, options);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Render a progessbar!
|
||||
*
|
||||
|
@ -16,218 +16,16 @@
|
||||
|
||||
/* exported
|
||||
printLabels,
|
||||
printPartLabels,
|
||||
printStockItemLabels,
|
||||
printStockLocationLabels,
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Perform the "print" action.
|
||||
/**
|
||||
* Present the user with the available labels,
|
||||
* and allow them to select which label to print.
|
||||
*
|
||||
* The intent is that the available labels have been requested
|
||||
* (via AJAX) from the server.
|
||||
*/
|
||||
function printLabels(url, plugin=null) {
|
||||
|
||||
if (plugin) {
|
||||
// If a plugin is provided, do not redirect the browser.
|
||||
// Instead, perform an API request and display a message
|
||||
|
||||
url = url + `plugin=${plugin}`;
|
||||
|
||||
inventreeGet(url, {}, {
|
||||
success: function(response) {
|
||||
showMessage(
|
||||
'{% trans "Labels sent to printer" %}',
|
||||
{
|
||||
style: 'success',
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
window.open(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
function printStockItemLabels(items) {
|
||||
/**
|
||||
* Print stock item labels for the given stock items
|
||||
*/
|
||||
|
||||
if (items.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Stock Items" %}',
|
||||
'{% trans "Stock item(s) must be selected before printing labels" %}'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request available labels from the server
|
||||
inventreeGet(
|
||||
'{% url "api-stockitem-label-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
items: items,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Labels Found" %}',
|
||||
'{% trans "No labels found which match selected stock item(s)" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select label to print
|
||||
selectLabel(
|
||||
response,
|
||||
items,
|
||||
{
|
||||
success: function(data) {
|
||||
|
||||
var pk = data.label;
|
||||
|
||||
var href = `/api/label/stock/${pk}/print/?`;
|
||||
|
||||
items.forEach(function(item) {
|
||||
href += `items[]=${item}&`;
|
||||
});
|
||||
|
||||
printLabels(href, data.plugin);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function printStockLocationLabels(locations) {
|
||||
|
||||
if (locations.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Stock Locations" %}',
|
||||
'{% trans "Stock location(s) must be selected before printing labels" %}'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request available labels from the server
|
||||
inventreeGet(
|
||||
'{% url "api-stocklocation-label-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
locations: locations,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Labels Found" %}',
|
||||
'{% trans "No labels found which match selected stock location(s)" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select label to print
|
||||
selectLabel(
|
||||
response,
|
||||
locations,
|
||||
{
|
||||
success: function(data) {
|
||||
|
||||
var pk = data.label;
|
||||
|
||||
var href = `/api/label/location/${pk}/print/?`;
|
||||
|
||||
locations.forEach(function(location) {
|
||||
href += `locations[]=${location}&`;
|
||||
});
|
||||
|
||||
printLabels(href, data.plugin);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function printPartLabels(parts) {
|
||||
/**
|
||||
* Print labels for the provided parts
|
||||
*/
|
||||
|
||||
if (parts.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Parts" %}',
|
||||
'{% trans "Part(s) must be selected before printing labels" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request available labels from the server
|
||||
inventreeGet(
|
||||
'{% url "api-part-label-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
parts: parts,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Labels Found" %}',
|
||||
'{% trans "No labels found which match the selected part(s)" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select label to print
|
||||
selectLabel(
|
||||
response,
|
||||
parts,
|
||||
{
|
||||
success: function(data) {
|
||||
|
||||
var pk = data.label;
|
||||
|
||||
var href = `/api/label/part/${pk}/print/?`;
|
||||
|
||||
parts.forEach(function(part) {
|
||||
href += `parts[]=${part}&`;
|
||||
});
|
||||
|
||||
printLabels(href, data.plugin);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function selectLabel(labels, items, options={}) {
|
||||
/**
|
||||
* Present the user with the available labels,
|
||||
* and allow them to select which label to print.
|
||||
*
|
||||
* The intent is that the available labels have been requested
|
||||
* (via AJAX) from the server.
|
||||
*/
|
||||
|
||||
// Array of available plugins for label printing
|
||||
var plugins = [];
|
||||
@ -347,3 +145,71 @@ function selectLabel(labels, items, options={}) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Print label(s) for the selected items:
|
||||
*
|
||||
* - Retrieve a list of matching label templates from the server
|
||||
* - Present the available templates to the user (if more than one available)
|
||||
* - Request printed labels
|
||||
*
|
||||
* Required options:
|
||||
* - url: The list URL for the particular template type
|
||||
* - items: The list of items to be printed
|
||||
* - key: The key to use in the query parameters
|
||||
*/
|
||||
function printLabels(options) {
|
||||
|
||||
if (!options.items || options.items.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Items" %}',
|
||||
'{% trans "No items selected for printing" %}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let params = {
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
params[options.key] = options.items;
|
||||
|
||||
// Request a list of available label templates
|
||||
inventreeGet(options.url, params, {
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Labels Found" %}',
|
||||
'{% trans "No label templates found which match the selected items" %}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Select label template for printing
|
||||
selectLabel(response, options.items, {
|
||||
success: function(data) {
|
||||
let href = `${options.url}${data.label}/print/?`;
|
||||
|
||||
options.items.forEach(function(item) {
|
||||
href += `${options.key}=${item}&`;
|
||||
});
|
||||
|
||||
if (data.plugin) {
|
||||
href += `plugin=${data.plugin}`;
|
||||
|
||||
inventreeGet(href, {}, {
|
||||
success: function(response) {
|
||||
showMessage('{% trans "Labels sent to printer" %}', {
|
||||
style: 'success',
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
window.open(href);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -10,11 +10,13 @@
|
||||
getModelRenderer,
|
||||
renderBuild,
|
||||
renderCompany,
|
||||
renderContact,
|
||||
renderGroup,
|
||||
renderManufacturerPart,
|
||||
renderOwner,
|
||||
renderPart,
|
||||
renderPartCategory,
|
||||
renderReturnOrder,
|
||||
renderStockItem,
|
||||
renderStockLocation,
|
||||
renderSupplierPart,
|
||||
@ -44,6 +46,8 @@ function getModelRenderer(model) {
|
||||
switch (model) {
|
||||
case 'company':
|
||||
return renderCompany;
|
||||
case 'contact':
|
||||
return renderContact;
|
||||
case 'stockitem':
|
||||
return renderStockItem;
|
||||
case 'stocklocation':
|
||||
@ -58,6 +62,8 @@ function getModelRenderer(model) {
|
||||
return renderPurchaseOrder;
|
||||
case 'salesorder':
|
||||
return renderSalesOrder;
|
||||
case 'returnorder':
|
||||
return renderReturnOrder;
|
||||
case 'salesordershipment':
|
||||
return renderSalesOrderShipment;
|
||||
case 'manufacturerpart':
|
||||
@ -150,6 +156,17 @@ function renderCompany(data, parameters={}) {
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "Contact" model
|
||||
function renderContact(data, parameters={}) {
|
||||
return renderModel(
|
||||
{
|
||||
text: data.name,
|
||||
},
|
||||
parameters
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "StockItem" model
|
||||
function renderStockItem(data, parameters={}) {
|
||||
|
||||
@ -158,9 +175,6 @@ function renderStockItem(data, parameters={}) {
|
||||
let render_location_detail = ('render_location_detail' in parameters) ? parameters.render_location_detail : false;
|
||||
let render_available_quantity = ('render_available_quantity' in parameters) ? parameters.render_available_quantity : false;
|
||||
|
||||
if (render_part_detail) {
|
||||
}
|
||||
|
||||
let text = '';
|
||||
let stock_detail = '';
|
||||
|
||||
@ -360,6 +374,26 @@ function renderSalesOrder(data, parameters={}) {
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "ReturnOrder" model
|
||||
function renderReturnOrder(data, parameters={}) {
|
||||
let image = blankImage();
|
||||
|
||||
if (data.customer_detail) {
|
||||
image = data.customer_detail.thumbnail || data.customer_detail.image || blankImage();
|
||||
}
|
||||
|
||||
return renderModel(
|
||||
{
|
||||
image: image,
|
||||
text: `${data.reference} - ${data.customer_detail.name}`,
|
||||
textSecondary: shortenString(data.description),
|
||||
url: data.url || `/order/return-order/${data.pk}/`,
|
||||
},
|
||||
parameters,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "SalesOrderShipment" model
|
||||
function renderSalesOrderShipment(data, parameters={}) {
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -12,7 +12,6 @@
|
||||
loadTableFilters,
|
||||
makeIconBadge,
|
||||
makeIconButton,
|
||||
printPartLabels,
|
||||
renderLink,
|
||||
setFormGroupVisibility,
|
||||
setupFilterList,
|
||||
@ -366,7 +365,7 @@ function createPart(options={}) {
|
||||
*/
|
||||
function editPart(pk) {
|
||||
|
||||
var url = `/api/part/${pk}/`;
|
||||
var url = `{% url "api-part-list" %}${pk}/`;
|
||||
|
||||
var fields = partFields({
|
||||
edit: true
|
||||
@ -397,7 +396,7 @@ function duplicatePart(pk, options={}) {
|
||||
}
|
||||
|
||||
// First we need all the part information
|
||||
inventreeGet(`/api/part/${pk}/`, {}, {
|
||||
inventreeGet(`{% url "api-part-list" %}${pk}/`, {}, {
|
||||
|
||||
success: function(data) {
|
||||
|
||||
@ -446,7 +445,7 @@ function duplicatePart(pk, options={}) {
|
||||
// Launch form to delete a part
|
||||
function deletePart(pk, options={}) {
|
||||
|
||||
inventreeGet(`/api/part/${pk}/`, {}, {
|
||||
inventreeGet(`{% url "api-part-list" %}${pk}/`, {}, {
|
||||
success: function(part) {
|
||||
if (part.active) {
|
||||
showAlertDialog(
|
||||
@ -473,7 +472,7 @@ function deletePart(pk, options={}) {
|
||||
</div>`;
|
||||
|
||||
constructForm(
|
||||
`/api/part/${pk}/`,
|
||||
`{% url "api-part-list" %}${pk}/`,
|
||||
{
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Part" %}',
|
||||
@ -542,7 +541,7 @@ function validateBom(part_id, options={}) {
|
||||
</div>
|
||||
`;
|
||||
|
||||
constructForm(`/api/part/${part_id}/bom-validate/`, {
|
||||
constructForm(`{% url "api-part-list" %}${part_id}/bom-validate/`, {
|
||||
method: 'PUT',
|
||||
fields: {
|
||||
valid: {},
|
||||
@ -560,7 +559,7 @@ function validateBom(part_id, options={}) {
|
||||
/* Duplicate a BOM */
|
||||
function duplicateBom(part_id, options={}) {
|
||||
|
||||
constructForm(`/api/part/${part_id}/bom-copy/`, {
|
||||
constructForm(`{% url "api-part-list" %}${part_id}/bom-copy/`, {
|
||||
method: 'POST',
|
||||
fields: {
|
||||
part: {
|
||||
@ -646,7 +645,7 @@ function partStockLabel(part, options={}) {
|
||||
var required_build_order_quantity = null;
|
||||
var required_sales_order_quantity = null;
|
||||
|
||||
inventreeGet(`/api/part/${part.pk}/requirements/`, {}, {
|
||||
inventreeGet(`{% url "api-part-list" %}${part.pk}/requirements/`, {}, {
|
||||
async: false,
|
||||
success: function(response) {
|
||||
required_build_order_quantity = 0;
|
||||
@ -953,11 +952,7 @@ function loadPartStocktakeTable(partId, options={}) {
|
||||
|
||||
params.part = partId;
|
||||
|
||||
var filters = loadTableFilters('stocktake');
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
var filters = loadTableFilters('stocktake', params);
|
||||
|
||||
setupFilterList('stocktake', $(table), '#filter-list-partstocktake');
|
||||
|
||||
@ -1024,19 +1019,17 @@ function loadPartStocktakeTable(partId, options={}) {
|
||||
switchable: false,
|
||||
sortable: false,
|
||||
formatter: function(value, row) {
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
let html = '';
|
||||
|
||||
if (options.allow_edit) {
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-edit-stocktake', row.pk, '{% trans "Edit Stocktake Entry" %}');
|
||||
html += makeEditButton('button-edit-stocktake', row.pk, '{% trans "Edit Stocktake Entry" %}');
|
||||
}
|
||||
|
||||
if (options.allow_delete) {
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-delete-stocktake', row.pk, '{% trans "Delete Stocktake Entry" %}');
|
||||
html += makeDeleteButton('button-delete-stocktake', row.pk, '{% trans "Delete Stocktake Entry" %}');
|
||||
}
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -1045,7 +1038,7 @@ function loadPartStocktakeTable(partId, options={}) {
|
||||
$(table).find('.button-edit-stocktake').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/part/stocktake/${pk}/`, {
|
||||
constructForm(`{% url "api-part-stocktake-list" %}${pk}/`, {
|
||||
fields: {
|
||||
item_count: {},
|
||||
quantity: {},
|
||||
@ -1066,21 +1059,17 @@ function loadPartStocktakeTable(partId, options={}) {
|
||||
},
|
||||
},
|
||||
title: '{% trans "Edit Stocktake Entry" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
|
||||
$(table).find('.button-delete-stocktake').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/part/stocktake/${pk}/`, {
|
||||
constructForm(`{% url "api-part-stocktake-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Stocktake Entry" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1088,7 +1077,8 @@ function loadPartStocktakeTable(partId, options={}) {
|
||||
}
|
||||
|
||||
|
||||
/* Load part variant table
|
||||
/*
|
||||
* Load part variant table
|
||||
*/
|
||||
function loadPartVariantTable(table, partId, options={}) {
|
||||
|
||||
@ -1097,11 +1087,7 @@ function loadPartVariantTable(table, partId, options={}) {
|
||||
params.ancestor = partId;
|
||||
|
||||
// Load filters
|
||||
var filters = loadTableFilters('variants');
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
var filters = loadTableFilters('variants', params);
|
||||
|
||||
setupFilterList('variants', $(table));
|
||||
|
||||
@ -1247,11 +1233,7 @@ function loadPartParameterTable(table, options) {
|
||||
var params = options.params || {};
|
||||
|
||||
// Load filters
|
||||
var filters = loadTableFilters('part-parameters');
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
var filters = loadTableFilters('part-parameters', params);
|
||||
|
||||
var filterTarget = options.filterTarget || '#filter-list-parameters';
|
||||
|
||||
@ -1302,16 +1284,13 @@ function loadPartParameterTable(table, options) {
|
||||
switchable: false,
|
||||
sortable: false,
|
||||
formatter: function(value, row) {
|
||||
var pk = row.pk;
|
||||
let pk = row.pk;
|
||||
let html = '';
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
html += makeEditButton('button-parameter-edit', pk, '{% trans "Edit parameter" %}');
|
||||
html += makeDeleteButton('button-parameter-delete', pk, '{% trans "Delete parameter" %}');
|
||||
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-parameter-edit', pk, '{% trans "Edit parameter" %}');
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-parameter-delete', pk, '{% trans "Delete parameter" %}');
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
}
|
||||
}
|
||||
],
|
||||
@ -1320,26 +1299,22 @@ function loadPartParameterTable(table, options) {
|
||||
$(table).find('.button-parameter-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/part/parameter/${pk}/`, {
|
||||
constructForm(`{% url "api-part-parameter-list" %}${pk}/`, {
|
||||
fields: {
|
||||
data: {},
|
||||
},
|
||||
title: '{% trans "Edit Parameter" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
|
||||
$(table).find('.button-parameter-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/part/parameter/${pk}/`, {
|
||||
constructForm(`{% url "api-part-parameter-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Parameter" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -1361,11 +1336,7 @@ function loadPartPurchaseOrderTable(table, part_id, options={}) {
|
||||
options.params.part_detail = true;
|
||||
options.params.order_detail = true;
|
||||
|
||||
var filters = loadTableFilters('purchaseorderlineitem');
|
||||
|
||||
for (var key in options.params) {
|
||||
filters[key] = options.params[key];
|
||||
}
|
||||
var filters = loadTableFilters('purchaseorderlineitem', options.params);
|
||||
|
||||
setupFilterList('purchaseorderlineitem', $(table), '#filter-list-partpurchaseorders');
|
||||
|
||||
@ -1474,12 +1445,16 @@ function loadPartPurchaseOrderTable(table, part_id, options={}) {
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
formatter: function(value, row) {
|
||||
var data = value;
|
||||
let data = value;
|
||||
|
||||
if (row.supplier_part_detail.pack_size != 1.0) {
|
||||
var pack_size = row.supplier_part_detail.pack_size;
|
||||
var total = value * pack_size;
|
||||
data += `<span class='fas fa-info-circle icon-blue float-right' title='{% trans "Pack Quantity" %}: ${pack_size} - {% trans "Total Quantity" %}: ${total}'></span>`;
|
||||
let pack_size = row.supplier_part_detail.pack_size;
|
||||
let total = value * pack_size;
|
||||
|
||||
data += makeIconBadge(
|
||||
'fa-info-circle icon-blue',
|
||||
`{% trans "Pack Quantity" %}: ${pack_size} - {% trans "Total Quantity" %}: ${total}`
|
||||
);
|
||||
}
|
||||
|
||||
return data;
|
||||
@ -1515,7 +1490,10 @@ function loadPartPurchaseOrderTable(table, part_id, options={}) {
|
||||
}
|
||||
|
||||
if (overdue) {
|
||||
html += `<span class='fas fa-calendar-alt icon-red float-right' title='{% trans "This line item is overdue" %}'></span>`;
|
||||
html += makeIconBadge(
|
||||
'fa-calendar-alt icon-red',
|
||||
'{% trans "This line item is overdue" %}',
|
||||
);
|
||||
}
|
||||
|
||||
return html;
|
||||
@ -1557,13 +1535,12 @@ function loadPartPurchaseOrderTable(table, part_id, options={}) {
|
||||
// Already recevied
|
||||
return `<span class='badge bg-success rounded-pill'>{% trans "Received" %}</span>`;
|
||||
} else if (row.order_detail && row.order_detail.status == {{ PurchaseOrderStatus.PLACED }}) {
|
||||
var html = `<div class='btn-group' role='group'>`;
|
||||
let html = '';
|
||||
var pk = row.pk;
|
||||
|
||||
html += makeIconButton('fa-sign-in-alt', 'button-line-receive', pk, '{% trans "Receive line item" %}');
|
||||
|
||||
html += `</div>`;
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
@ -1627,14 +1604,10 @@ function loadRelatedPartsTable(table, part_id, options={}) {
|
||||
title: '',
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
let html = '';
|
||||
html += makeDeleteButton('button-related-delete', row.pk, '{% trans "Delete part relationship" %}');
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-related-delete', row.pk, '{% trans "Delete part relationship" %}');
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
}
|
||||
}
|
||||
];
|
||||
@ -1652,12 +1625,10 @@ function loadRelatedPartsTable(table, part_id, options={}) {
|
||||
$(table).find('.button-related-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/part/related/${pk}/`, {
|
||||
constructForm(`{% url "api-part-related-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Part Relationship" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
},
|
||||
@ -1833,17 +1804,15 @@ function loadPartTable(table, url, options={}) {
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
var filters = {};
|
||||
var filters = loadTableFilters('parts', options.params);
|
||||
|
||||
if (!options.disableFilters) {
|
||||
filters = loadTableFilters('parts');
|
||||
}
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
setupFilterList('parts', $(table), options.filterTarget, {download: true});
|
||||
setupFilterList('parts', $(table), options.filterTarget, {
|
||||
download: true,
|
||||
labels: {
|
||||
url: '{% url "api-part-label-list" %}',
|
||||
key: 'part',
|
||||
}
|
||||
});
|
||||
|
||||
var columns = [
|
||||
{
|
||||
@ -2153,7 +2122,7 @@ function loadPartTable(table, url, options={}) {
|
||||
var part = parts.shift();
|
||||
|
||||
inventreePut(
|
||||
`/api/part/${part}/`,
|
||||
`{% url "api-part-list" %}${part}/`,
|
||||
{
|
||||
category: category,
|
||||
},
|
||||
@ -2176,19 +2145,6 @@ function loadPartTable(table, url, options={}) {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// Callback function for the "print label" button
|
||||
$('#multi-part-print-label').click(function() {
|
||||
var selections = getTableData(table);
|
||||
|
||||
var items = [];
|
||||
|
||||
selections.forEach(function(item) {
|
||||
items.push(item.pk);
|
||||
});
|
||||
|
||||
printPartLabels(items);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -2201,15 +2157,8 @@ function loadPartCategoryTable(table, options) {
|
||||
|
||||
var filterListElement = options.filterList || '#filter-list-category';
|
||||
|
||||
var filters = {};
|
||||
|
||||
var filterKey = options.filterKey || options.name || 'category';
|
||||
|
||||
if (!options.disableFilters) {
|
||||
filters = loadTableFilters(filterKey);
|
||||
}
|
||||
|
||||
|
||||
var tree_view = options.allowTreeView && inventreeLoad('category-tree-view') == 1;
|
||||
|
||||
if (tree_view) {
|
||||
@ -2217,12 +2166,7 @@ function loadPartCategoryTable(table, options) {
|
||||
params.depth = global_settings.INVENTREE_TREE_DEPTH;
|
||||
}
|
||||
|
||||
var original = {};
|
||||
|
||||
for (var key in params) {
|
||||
original[key] = params[key];
|
||||
filters[key] = params[key];
|
||||
}
|
||||
let filters = loadTableFilters(filterKey, params);
|
||||
|
||||
setupFilterList(filterKey, table, filterListElement, {download: true});
|
||||
|
||||
@ -2270,7 +2214,7 @@ function loadPartCategoryTable(table, options) {
|
||||
serverSort: !tree_view,
|
||||
search: !tree_view,
|
||||
name: 'category',
|
||||
original: original,
|
||||
original: params,
|
||||
showColumns: true,
|
||||
sortable: true,
|
||||
buttons: options.allowTreeView ? [
|
||||
@ -2461,13 +2405,7 @@ function loadPartTestTemplateTable(table, options) {
|
||||
|
||||
var filterListElement = options.filterList || '#filter-list-parttests';
|
||||
|
||||
var filters = loadTableFilters('parttests');
|
||||
|
||||
var original = {};
|
||||
|
||||
for (var k in params) {
|
||||
original[k] = params[k];
|
||||
}
|
||||
var filters = loadTableFilters('parttests', params);
|
||||
|
||||
setupFilterList('parttests', table, filterListElement);
|
||||
|
||||
@ -2484,7 +2422,7 @@ function loadPartTestTemplateTable(table, options) {
|
||||
url: '{% url "api-part-test-template-list" %}',
|
||||
queryParams: filters,
|
||||
name: 'testtemplate',
|
||||
original: original,
|
||||
original: params,
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
@ -2528,14 +2466,12 @@ function loadPartTestTemplateTable(table, options) {
|
||||
var pk = row.pk;
|
||||
|
||||
if (row.part == part) {
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
let html = '';
|
||||
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-test-edit', pk, '{% trans "Edit test result" %}');
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-test-delete', pk, '{% trans "Delete test result" %}');
|
||||
html += makeEditButton('button-test-edit', pk, '{% trans "Edit test result" %}');
|
||||
html += makeDeleteButton('button-test-delete', pk, '{% trans "Delete test result" %}');
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
} else {
|
||||
var text = '{% trans "This test is defined for a parent part" %}';
|
||||
|
||||
@ -2568,9 +2504,7 @@ function loadPartTestTemplateTable(table, options) {
|
||||
constructForm(url, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Test Result Template" %}',
|
||||
onSuccess: function() {
|
||||
table.bootstrapTable('refresh');
|
||||
},
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -2593,7 +2527,7 @@ function loadPartSchedulingChart(canvas_id, part_id) {
|
||||
var was_error = false;
|
||||
|
||||
// First, grab updated data for the particular part
|
||||
inventreeGet(`/api/part/${part_id}/`, {}, {
|
||||
inventreeGet(`{% url "api-part-list" %}${part_id}/`, {}, {
|
||||
async: false,
|
||||
success: function(response) {
|
||||
part_info = response;
|
||||
@ -2632,7 +2566,7 @@ function loadPartSchedulingChart(canvas_id, part_id) {
|
||||
* and arranged in increasing chronological order
|
||||
*/
|
||||
inventreeGet(
|
||||
`/api/part/${part_id}/scheduling/`,
|
||||
`{% url "api-part-list" %}${part_id}/scheduling/`,
|
||||
{},
|
||||
{
|
||||
async: false,
|
||||
@ -2649,15 +2583,15 @@ function loadPartSchedulingChart(canvas_id, part_id) {
|
||||
|
||||
if (date == null) {
|
||||
date_string = '<em>{% trans "No date specified" %}</em>';
|
||||
date_string += `<span class='fas fa-exclamation-circle icon-red float-right' title='{% trans "No date specified" %}'></span>`;
|
||||
date_string += makeIconBadge('fa-exclamation-circle icon-red', '{% trans "No date specified" %}');
|
||||
} else if (date < today) {
|
||||
date_string += `<span class='fas fa-exclamation-circle icon-yellow float-right' title='{% trans "Specified date is in the past" %}'></span>`;
|
||||
date_string += makeIconBadge('fa-exclamation-circle icon-yellow', '{% trans "Specified date is in the past" %}');
|
||||
}
|
||||
|
||||
var quantity_string = entry.quantity + entry.speculative_quantity;
|
||||
|
||||
if (entry.speculative_quantity != 0) {
|
||||
quantity_string += `<span class='fas fa-question-circle icon-blue float-right' title='{% trans "Speculative" %}'></span>`;
|
||||
quantity_string += makeIconBadge('fa-question-circle icon-blue', '{% trans "Speculative" %}');
|
||||
}
|
||||
|
||||
// Add an entry to the scheduling table
|
||||
|
@ -603,14 +603,14 @@ function loadPriceBreakTable(table, options={}) {
|
||||
title: '{% trans "Price" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
var html = formatCurrency(value, {currency: row.price_currency});
|
||||
let html = formatCurrency(value, {currency: row.price_currency});
|
||||
|
||||
html += `<div class='btn-group float-right' role='group'>`;
|
||||
let buttons = '';
|
||||
|
||||
html += makeIconButton('fa-edit icon-blue', `button-${name}-edit`, row.pk, `{% trans "Edit ${human_name}" %}`);
|
||||
html += makeIconButton('fa-trash-alt icon-red', `button-${name}-delete`, row.pk, `{% trans "Delete ${human_name}" %}`);
|
||||
buttons += makeEditButton(`button-${name}-edit`, row.pk, `{% trans "Edit ${human_name}" %}`);
|
||||
buttons += makeDeleteButton(`button-${name}-delete`, row.pk, `{% trans "Delete ${human_name}" %}`);
|
||||
|
||||
html += `</div>`;
|
||||
html += wrapButtons(buttons);
|
||||
|
||||
return html;
|
||||
}
|
||||
|
2068
InvenTree/templates/js/translated/purchase_order.js
Normal file
2068
InvenTree/templates/js/translated/purchase_order.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -14,21 +14,17 @@
|
||||
*/
|
||||
|
||||
/* exported
|
||||
printBomReports,
|
||||
printBuildReports,
|
||||
printPurchaseOrderReports,
|
||||
printSalesOrderReports,
|
||||
printTestReports,
|
||||
printReports,
|
||||
*/
|
||||
|
||||
/**
|
||||
* Present the user with the available reports,
|
||||
* and allow them to select which report to print.
|
||||
*
|
||||
* The intent is that the available report templates have been requested
|
||||
* (via AJAX) from the server.
|
||||
*/
|
||||
function selectReport(reports, items, options={}) {
|
||||
/**
|
||||
* Present the user with the available reports,
|
||||
* and allow them to select which report to print.
|
||||
*
|
||||
* The intent is that the available report templates have been requested
|
||||
* (via AJAX) from the server.
|
||||
*/
|
||||
|
||||
// If there is only a single report available, just print!
|
||||
if (reports.length == 1) {
|
||||
@ -108,270 +104,57 @@ function selectReport(reports, items, options={}) {
|
||||
}
|
||||
|
||||
|
||||
function printTestReports(items) {
|
||||
/**
|
||||
* Print test reports for the provided stock item(s)
|
||||
*/
|
||||
/*
|
||||
* Print report(s) for the selected items:
|
||||
*
|
||||
* - Retrieve a list of matching report templates from the server
|
||||
* - Present the available templates to the user (if more than one available)
|
||||
* - Request printed document
|
||||
*
|
||||
* Required options:
|
||||
* - url: The list URL for the particular template type
|
||||
* - items: The list of objects to print
|
||||
* - key: The key to use in the query parameters
|
||||
*/
|
||||
function printReports(options) {
|
||||
|
||||
if (items.length == 0) {
|
||||
if (!options.items || options.items.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Stock Items" %}',
|
||||
'{% trans "Stock item(s) must be selected before printing reports" %}'
|
||||
'{% trans "Select Items" %}',
|
||||
'{% trans "No items selected for printing" }',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request available reports from the server
|
||||
inventreeGet(
|
||||
'{% url "api-stockitem-testreport-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
items: items,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match selected stock item(s)" %}',
|
||||
);
|
||||
let params = {
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
params[options.key] = options.items;
|
||||
|
||||
// Select report template to print
|
||||
selectReport(
|
||||
response,
|
||||
items,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/report/test/${pk}/print/?`;
|
||||
|
||||
items.forEach(function(item) {
|
||||
href += `item=${item}&`;
|
||||
});
|
||||
|
||||
window.open(href);
|
||||
}
|
||||
}
|
||||
// Request a list of available report templates
|
||||
inventreeGet(options.url, params, {
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match the selected items" %}',
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Select report template for printing
|
||||
selectReport(response, options.items, {
|
||||
success: function(pk) {
|
||||
let href = `${options.url}${pk}/print/?`;
|
||||
|
||||
function printBuildReports(builds) {
|
||||
/**
|
||||
* Print Build report for the provided build(s)
|
||||
*/
|
||||
options.items.forEach(function(item) {
|
||||
href += `${options.key}=${item}&`;
|
||||
});
|
||||
|
||||
if (builds.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Builds" %}',
|
||||
'{% trans "Build(s) must be selected before printing reports" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
inventreeGet(
|
||||
'{% url "api-build-report-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
builds: builds,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match selected build(s)" %}'
|
||||
);
|
||||
|
||||
return;
|
||||
window.open(href);
|
||||
}
|
||||
|
||||
// Select which report to print
|
||||
selectReport(
|
||||
response,
|
||||
builds,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/report/build/${pk}/print/?`;
|
||||
|
||||
builds.forEach(function(build) {
|
||||
href += `build=${build}&`;
|
||||
});
|
||||
|
||||
window.open(href);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function printBomReports(parts) {
|
||||
/**
|
||||
* Print BOM reports for the provided part(s)
|
||||
*/
|
||||
|
||||
if (parts.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Parts" %}',
|
||||
'{% trans "Part(s) must be selected before printing reports" %}'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request available reports from the server
|
||||
inventreeGet(
|
||||
'{% url "api-bom-report-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
parts: parts,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match selected part(s)" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select which report to print
|
||||
selectReport(
|
||||
response,
|
||||
parts,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/report/bom/${pk}/print/?`;
|
||||
|
||||
parts.forEach(function(part) {
|
||||
href += `part=${part}&`;
|
||||
});
|
||||
|
||||
window.open(href);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function printPurchaseOrderReports(orders) {
|
||||
/**
|
||||
* Print PurchaseOrder reports for the provided purchase order(s)
|
||||
*/
|
||||
|
||||
if (orders.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Purchase Orders" %}',
|
||||
'{% trans "Purchase Order(s) must be selected before printing report" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request avaiable report templates
|
||||
inventreeGet(
|
||||
'{% url "api-po-report-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
orders: orders,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match selected orders" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select report template
|
||||
selectReport(
|
||||
response,
|
||||
orders,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/report/po/${pk}/print/?`;
|
||||
|
||||
orders.forEach(function(order) {
|
||||
href += `order=${order}&`;
|
||||
});
|
||||
|
||||
window.open(href);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function printSalesOrderReports(orders) {
|
||||
/**
|
||||
* Print SalesOrder reports for the provided purchase order(s)
|
||||
*/
|
||||
|
||||
if (orders.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Sales Orders" %}',
|
||||
'{% trans "Sales Order(s) must be selected before printing report" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request avaiable report templates
|
||||
inventreeGet(
|
||||
'{% url "api-so-report-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
orders: orders,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match selected orders" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select report template
|
||||
selectReport(
|
||||
response,
|
||||
orders,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/report/so/${pk}/print/?`;
|
||||
|
||||
orders.forEach(function(order) {
|
||||
href += `order=${order}&`;
|
||||
});
|
||||
|
||||
window.open(href);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
743
InvenTree/templates/js/translated/return_order.js
Normal file
743
InvenTree/templates/js/translated/return_order.js
Normal file
@ -0,0 +1,743 @@
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
/* globals
|
||||
companyFormFields,
|
||||
constructForm,
|
||||
imageHoverIcon,
|
||||
loadTableFilters,
|
||||
renderLink,
|
||||
returnOrderStatusDisplay,
|
||||
setupFilterList,
|
||||
*/
|
||||
|
||||
/* exported
|
||||
cancelReturnOrder,
|
||||
completeReturnOrder,
|
||||
createReturnOrder,
|
||||
createReturnOrderLineItem,
|
||||
editReturnOrder,
|
||||
editReturnOrderLineItem,
|
||||
issueReturnOrder,
|
||||
loadReturnOrderTable,
|
||||
loadReturnOrderLineItemTable,
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Construct a set of fields for a ReturnOrder form
|
||||
*/
|
||||
function returnOrderFields(options={}) {
|
||||
|
||||
let fields = {
|
||||
reference: {
|
||||
icon: 'fa-hashtag',
|
||||
},
|
||||
description: {},
|
||||
customer: {
|
||||
icon: 'fa-user-tie',
|
||||
secondary: {
|
||||
title: '{% trans "Add Customer" %}',
|
||||
fields: function() {
|
||||
var fields = companyFormFields();
|
||||
fields.is_customer.value = true;
|
||||
return fields;
|
||||
}
|
||||
}
|
||||
},
|
||||
customer_reference: {},
|
||||
target_date: {
|
||||
icon: 'fa-calendar-alt',
|
||||
},
|
||||
link: {
|
||||
icon: 'fa-link',
|
||||
},
|
||||
contact: {
|
||||
icon: 'fa-user',
|
||||
adjustFilters: function(filters) {
|
||||
let customer = getFormFieldValue('customer', {}, {modal: options.modal});
|
||||
|
||||
if (customer) {
|
||||
filters.company = customer;
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
},
|
||||
responsible: {
|
||||
icon: 'fa-user',
|
||||
}
|
||||
};
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a new Return Order
|
||||
*/
|
||||
function createReturnOrder(options={}) {
|
||||
let fields = returnOrderFields(options);
|
||||
|
||||
if (options.customer) {
|
||||
fields.customer.value = options.customer;
|
||||
}
|
||||
|
||||
constructForm('{% url "api-return-order-list" %}', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
title: '{% trans "Create Return Order" %}',
|
||||
onSuccess: function(data) {
|
||||
location.href = `/order/return-order/${data.pk}/`;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Edit an existing Return Order
|
||||
*/
|
||||
function editReturnOrder(order_id, options={}) {
|
||||
|
||||
constructForm(`{% url "api-return-order-list" %}${order_id}/`, {
|
||||
fields: returnOrderFields(options),
|
||||
title: '{% trans "Edit Return Order" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* "Issue" a ReturnOrder, to mark it as "in progress"
|
||||
*/
|
||||
function issueReturnOrder(order_id, options={}) {
|
||||
|
||||
let html = `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans 'After placing this order, line items will no longer be editable.' %}
|
||||
</div>`;
|
||||
|
||||
constructForm(`{% url "api-return-order-list" %}${order_id}/issue/`, {
|
||||
method: 'POST',
|
||||
title: '{% trans "Issue Return Order" %}',
|
||||
confirm: true,
|
||||
preFormContent: html,
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launches a modal form to cancel a ReturnOrder
|
||||
*/
|
||||
function cancelReturnOrder(order_id, options={}) {
|
||||
|
||||
let html = `
|
||||
<div class='alert alert-danger alert-block'>
|
||||
{% trans "Are you sure you wish to cancel this Return Order?" %}
|
||||
</div>`;
|
||||
|
||||
constructForm(
|
||||
`{% url "api-return-order-list" %}${order_id}/cancel/`,
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Cancel Return Order" %}',
|
||||
confirm: true,
|
||||
preFormContent: html,
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launches a modal form to mark a ReturnOrder as "complete"
|
||||
*/
|
||||
function completeReturnOrder(order_id, options={}) {
|
||||
let html = `
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "Mark this order as complete?" %}
|
||||
</div>
|
||||
`;
|
||||
|
||||
constructForm(
|
||||
`{% url "api-return-order-list" %}${order_id}/complete/`,
|
||||
{
|
||||
method: 'POST',
|
||||
title: '{% trans "Complete Return Order" %}',
|
||||
confirm: true,
|
||||
preFormContent: html,
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load a table of return orders
|
||||
*/
|
||||
function loadReturnOrderTable(table, options={}) {
|
||||
|
||||
// Ensure the table starts in a known state
|
||||
$(table).bootstrapTable('destroy');
|
||||
|
||||
options.params = options.params || {};
|
||||
options.params['customer_detail'] = true;
|
||||
|
||||
let filters = loadTableFilters('returnorder', options.params);
|
||||
|
||||
setupFilterList('returnorder', $(table), '#filter-list-returnorder', {
|
||||
download: true,
|
||||
report: {
|
||||
url: '{% url "api-return-order-report-list" %}',
|
||||
key: 'order',
|
||||
}
|
||||
});
|
||||
|
||||
let display_mode = inventreeLoad('returnorder-table-display-mode', 'list');
|
||||
|
||||
let is_calendar = display_mode == 'calendar';
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: '{% url "api-return-order-list" %}',
|
||||
queryParams: filters,
|
||||
name: 'returnorder',
|
||||
sidePagination: 'server',
|
||||
original: options.params,
|
||||
showColumns: !is_calendar,
|
||||
search: !is_calendar,
|
||||
showCustomViewButton: false,
|
||||
showCustomView: is_calendar,
|
||||
disablePagination: is_calendar,
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No return orders found" %}';
|
||||
},
|
||||
onRefresh: function() {
|
||||
loadReturnOrderTable(table, options);
|
||||
},
|
||||
onLoadSuccess: function() {
|
||||
// TODO
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
title: '',
|
||||
checkbox: true,
|
||||
visible: true,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'reference',
|
||||
title: '{% trans "Return Order" %}',
|
||||
formatter: function(value, row) {
|
||||
let html = renderLink(value, `/order/return-order/${row.pk}/`);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "Order is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
},
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
sortName: 'customer__name',
|
||||
field: 'customer_detail',
|
||||
title: '{% trans "Customer" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
if (!row.customer_detail) {
|
||||
return '{% trans "Invalid Customer" %}';
|
||||
}
|
||||
|
||||
return imageHoverIcon(row.customer_detail.image) + renderLink(row.customer_detail.name, `/company/${row.customer}/sales-orders/`);
|
||||
}
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'customer_reference',
|
||||
title: '{% trans "Customer Reference" %}',
|
||||
},
|
||||
{
|
||||
sortable: false,
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'status',
|
||||
title: '{% trans "Status" %}',
|
||||
formatter: function(value, row) {
|
||||
return returnOrderStatusDisplay(row.status);
|
||||
}
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'creation_date',
|
||||
title: '{% trans "Creation Date" %}',
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
}
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'target_date',
|
||||
title: '{% trans "Target Date" %}',
|
||||
formatter: function(value) {
|
||||
return renderDate(value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'line_items',
|
||||
title: '{% trans "Items" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'responsible',
|
||||
title: '{% trans "Responsible" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
if (!row.responsible_detail) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
let html = row.responsible_detail.name;
|
||||
|
||||
if (row.responsible_detail.label == 'group') {
|
||||
html += `<span class='float-right fas fa-users'></span>`;
|
||||
} else {
|
||||
html += `<span class='float-right fas fa-user'></span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
// TODO: Add in the 'total cost' field
|
||||
field: 'total_price',
|
||||
title: '{% trans "Total Cost" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
visible: false,
|
||||
formatter: function(value, row) {
|
||||
return formatCurrency(value, {
|
||||
currency: row.total_price_currency
|
||||
});
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a set of fields for a ReturnOrderLineItem form
|
||||
*/
|
||||
function returnOrderLineItemFields(options={}) {
|
||||
|
||||
let fields = {
|
||||
order: {
|
||||
filters: {
|
||||
customer_detail: true,
|
||||
}
|
||||
},
|
||||
item: {
|
||||
filters: {
|
||||
part_detail: true,
|
||||
serialized: true,
|
||||
}
|
||||
},
|
||||
reference: {},
|
||||
outcome: {
|
||||
icon: 'fa-route',
|
||||
},
|
||||
price: {
|
||||
icon: 'fa-dollar-sign',
|
||||
},
|
||||
price_currency: {
|
||||
icon: 'fa-coins',
|
||||
},
|
||||
target_date: {
|
||||
icon: 'fa-calendar-alt',
|
||||
},
|
||||
notes: {
|
||||
icon: 'fa-sticky-note',
|
||||
}
|
||||
};
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a new ReturnOrderLineItem
|
||||
*/
|
||||
function createReturnOrderLineItem(options={}) {
|
||||
|
||||
let fields = returnOrderLineItemFields();
|
||||
|
||||
if (options.order) {
|
||||
fields.order.value = options.order;
|
||||
fields.order.hidden = true;
|
||||
}
|
||||
|
||||
if (options.customer) {
|
||||
Object.assign(fields.item.filters, {
|
||||
customer: options.customer
|
||||
});
|
||||
}
|
||||
|
||||
constructForm('{% url "api-return-order-line-list" %}', {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Line Item" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Edit an existing ReturnOrderLineItem
|
||||
*/
|
||||
function editReturnOrderLineItem(pk, options={}) {
|
||||
|
||||
let fields = returnOrderLineItemFields();
|
||||
|
||||
constructForm(`{% url "api-return-order-line-list" %}${pk}/`, {
|
||||
fields: fields,
|
||||
title: '{% trans "Edit Line Item" %}',
|
||||
onSuccess: function(response) {
|
||||
handleFormSuccess(response, options);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Receive one or more items against a ReturnOrder
|
||||
*/
|
||||
function receiveReturnOrderItems(order_id, line_items, options={}) {
|
||||
|
||||
if (line_items.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Line Items"% }',
|
||||
'{% trans "At least one line item must be selected" %}'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
function renderLineItem(line_item) {
|
||||
let pk = line_item.pk;
|
||||
|
||||
// Render thumbnail + description
|
||||
let thumb = thumbnailImage(line_item.part_detail.thumbnail);
|
||||
|
||||
let buttons = '';
|
||||
|
||||
if (line_items.length > 1) {
|
||||
buttons += makeRemoveButton('button-row-remove', pk, '{% trans "Remove row" %}');
|
||||
}
|
||||
|
||||
buttons = wrapButtons(buttons);
|
||||
|
||||
let html = `
|
||||
<tr id='receive_row_${pk}' class='stock-receive-row'>
|
||||
<td id='part_${pk}'>
|
||||
${thumb} ${line_item.part_detail.full_name}
|
||||
</td>
|
||||
<td id='item_${pk}'>
|
||||
${line_item.item_detail.serial}
|
||||
</td>
|
||||
<td id='actions_${pk}'>${buttons}</td>
|
||||
</tr>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
let table_entries = '';
|
||||
|
||||
line_items.forEach(function(item) {
|
||||
if (!item.received_date) {
|
||||
table_entries += renderLineItem(item);
|
||||
}
|
||||
});
|
||||
|
||||
let html = '';
|
||||
|
||||
html += `
|
||||
<table class='table table-striped table-condensed' id='order-receive-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Serial Number" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>${table_entries}</tbody>
|
||||
</table>`;
|
||||
|
||||
constructForm(`{% url "api-return-order-list" %}${order_id}/receive/`, {
|
||||
method: 'POST',
|
||||
preFormContent: html,
|
||||
fields: {
|
||||
location: {
|
||||
filters: {
|
||||
strucutral: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
confirm: true,
|
||||
confirmMessage: '{% trans "Confirm receipt of items" %}',
|
||||
title: '{% trans "Receive Return Order Items" %}',
|
||||
afterRender: function(fields, opts) {
|
||||
// Add callback to remove rows
|
||||
$(opts.modal).find('.button-row-remove').click(function() {
|
||||
let pk = $(this).attr('pk');
|
||||
$(opts.modal).find(`#receive_row_${pk}`).remove();
|
||||
});
|
||||
},
|
||||
onSubmit: function(fields, opts) {
|
||||
// Extract data elements from the form
|
||||
let data = {
|
||||
items: [],
|
||||
location: getFormFieldValue('location', {}, opts),
|
||||
};
|
||||
|
||||
let item_pk_values = [];
|
||||
|
||||
line_items.forEach(function(item) {
|
||||
let pk = item.pk;
|
||||
let row = $(opts.modal).find(`#receive_row_${pk}`);
|
||||
|
||||
if (row.exists()) {
|
||||
data.items.push({
|
||||
item: pk,
|
||||
});
|
||||
item_pk_values.push(pk);
|
||||
}
|
||||
});
|
||||
|
||||
opts.nested = {
|
||||
'items': item_pk_values,
|
||||
};
|
||||
|
||||
inventreePut(
|
||||
opts.url,
|
||||
data,
|
||||
{
|
||||
method: 'POST',
|
||||
success: function(response) {
|
||||
$(opts.modal).modal('hide');
|
||||
|
||||
handleFormSuccess(response, options);
|
||||
},
|
||||
error: function(xhr) {
|
||||
switch (xhr.status) {
|
||||
case 400:
|
||||
handleFormErrors(xhr.responseJSON, fields, opts);
|
||||
break;
|
||||
default:
|
||||
$(opts.modal).modal('hide');
|
||||
showApiError(xhr, opts.url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load a table displaying line items for a particular ReturnOrder
|
||||
*/
|
||||
function loadReturnOrderLineItemTable(options={}) {
|
||||
|
||||
var table = options.table;
|
||||
|
||||
options.params = options.params || {};
|
||||
|
||||
options.params.order = options.order;
|
||||
options.params.item_detail = true;
|
||||
options.params.order_detail = false;
|
||||
options.params.part_detail = true;
|
||||
|
||||
let filters = loadTableFilters('returnorderlineitem', options.params);
|
||||
|
||||
setupFilterList('returnorderlineitem', $(table), '#filter-list-returnorderlines', {download: true});
|
||||
|
||||
function setupCallbacks() {
|
||||
if (options.allow_edit) {
|
||||
|
||||
// Callback for "receive" button
|
||||
if (options.allow_receive) {
|
||||
$(table).find('.button-line-receive').click(function() {
|
||||
let pk = $(this).attr('pk');
|
||||
|
||||
let line = $(table).bootstrapTable('getRowByUniqueId', pk);
|
||||
|
||||
receiveReturnOrderItems(
|
||||
options.order,
|
||||
[line],
|
||||
{
|
||||
onSuccess: function(response) {
|
||||
reloadBootstrapTable(table);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// Callback for "edit" button
|
||||
$(table).find('.button-line-edit').click(function() {
|
||||
let pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`{% url "api-return-order-line-list" %}${pk}/`, {
|
||||
fields: returnOrderLineItemFields(),
|
||||
title: '{% trans "Edit Line Item" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (options.allow_delete) {
|
||||
// Callback for "delete" button
|
||||
$(table).find('.button-line-delete').click(function() {
|
||||
let pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`{% url "api-return-order-line-list" %}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Line Item" %}',
|
||||
refreshTable: table,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: '{% url "api-return-order-line-list" %}',
|
||||
name: 'returnorderlineitems',
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No matching line items" %}';
|
||||
},
|
||||
onPostBody: setupCallbacks,
|
||||
queryParams: filters,
|
||||
original: options.params,
|
||||
showColumns: true,
|
||||
showFooter: true,
|
||||
uniqueId: 'pk',
|
||||
columns: [
|
||||
{
|
||||
checkbox: true,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'part',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
title: '{% trans "Part" %}',
|
||||
formatter: function(value, row) {
|
||||
let part = row.part_detail;
|
||||
let html = thumbnailImage(part.thumbnail) + ' ';
|
||||
html += renderLink(part.full_name, `/part/${part.pk}/`);
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'item',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
title: '{% trans "Item" %}',
|
||||
formatter: function(value, row) {
|
||||
return renderLink(`{% trans "Serial Number" %}: ${row.item_detail.serial}`, `/stock/item/${row.item}/`);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'reference',
|
||||
title: '{% trans "Reference" %}',
|
||||
},
|
||||
{
|
||||
field: 'outcome',
|
||||
title: '{% trans "Outcome" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
return returnOrderLineItemStatusDisplay(value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'price',
|
||||
title: '{% trans "Price" %}',
|
||||
formatter: function(value, row) {
|
||||
return formatCurrency(row.price, {
|
||||
currency: row.price_currency,
|
||||
});
|
||||
}
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'target_date',
|
||||
title: '{% trans "Target Date" %}',
|
||||
formatter: function(value, row) {
|
||||
let html = renderDate(value);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "This line item is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'received_date',
|
||||
title: '{% trans "Received" %}',
|
||||
sortable: true,
|
||||
formatter: function(value) {
|
||||
if (!value) {
|
||||
yesNoLabel(value);
|
||||
} else {
|
||||
return renderDate(value);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'notes',
|
||||
title: '{% trans "Notes" %}',
|
||||
},
|
||||
{
|
||||
field: 'buttons',
|
||||
title: '',
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
let buttons = '';
|
||||
let pk = row.pk;
|
||||
|
||||
if (options.allow_edit) {
|
||||
|
||||
if (options.allow_receive && !row.received_date) {
|
||||
buttons += makeIconButton('fa-sign-in-alt icon-green', 'button-line-receive', pk, '{% trans "Mark item as received" %}');
|
||||
}
|
||||
|
||||
buttons += makeEditButton('button-line-edit', pk, '{% trans "Edit line item" %}');
|
||||
}
|
||||
|
||||
if (options.allow_delete) {
|
||||
buttons += makeDeleteButton('button-line-delete', pk, '{% trans "Delete line item" %}');
|
||||
}
|
||||
|
||||
return wrapButtons(buttons);
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
2107
InvenTree/templates/js/translated/sales_order.js
Normal file
2107
InvenTree/templates/js/translated/sales_order.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -222,7 +222,7 @@ function updateSearch() {
|
||||
|
||||
if (checkPermission('purchase_order') && user_settings.SEARCH_PREVIEW_SHOW_PURCHASE_ORDERS) {
|
||||
|
||||
var filters = {
|
||||
let filters = {
|
||||
supplier_detail: true,
|
||||
};
|
||||
|
||||
@ -235,7 +235,7 @@ function updateSearch() {
|
||||
|
||||
if (checkPermission('sales_order') && user_settings.SEARCH_PREVIEW_SHOW_SALES_ORDERS) {
|
||||
|
||||
var filters = {
|
||||
let filters = {
|
||||
customer_detail: true,
|
||||
};
|
||||
|
||||
@ -247,6 +247,19 @@ function updateSearch() {
|
||||
addSearchQuery('salesorder', '{% trans "Sales Orders" %}', filters);
|
||||
}
|
||||
|
||||
if (checkPermission('return_order') && user_settings.SEARCH_PREVIEW_SHOW_RETURN_ORDERS) {
|
||||
let filters = {
|
||||
customer_detail: true,
|
||||
};
|
||||
|
||||
// Hide inactive (not "outstanding" orders)
|
||||
if (user_settings.SEARCH_PREVIEW_EXCLUDE_INACTIVE_RETURN_ORDERS) {
|
||||
filters.outstanding = true;
|
||||
}
|
||||
|
||||
addSearchQuery('returnorder', '{% trans "Return Orders" %}', filters);
|
||||
}
|
||||
|
||||
let ctx = $('#offcanvas-search').find('#search-context');
|
||||
|
||||
ctx.html(`
|
||||
|
24
InvenTree/templates/js/translated/status_codes.js
Normal file
24
InvenTree/templates/js/translated/status_codes.js
Normal file
@ -0,0 +1,24 @@
|
||||
{% load i18n %}
|
||||
{% load status_codes %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
/* globals
|
||||
*/
|
||||
|
||||
/* exported
|
||||
buildStatusDisplay,
|
||||
purchaseOrderStatusDisplay,
|
||||
returnOrderStatusDisplay,
|
||||
returnOrderLineItemStatusDisplay,
|
||||
salesOrderStatusDisplay,
|
||||
stockHistoryStatusDisplay,
|
||||
stockStatusDisplay,
|
||||
*/
|
||||
|
||||
{% include "status_codes.html" with label='stock' options=StockStatus.list %}
|
||||
{% include "status_codes.html" with label='stockHistory' options=StockHistoryCode.list %}
|
||||
{% include "status_codes.html" with label='build' options=BuildStatus.list %}
|
||||
{% include "status_codes.html" with label='purchaseOrder' options=PurchaseOrderStatus.list %}
|
||||
{% include "status_codes.html" with label='salesOrder' options=SalesOrderStatus.list %}
|
||||
{% include "status_codes.html" with label='returnOrder' options=ReturnOrderStatus.list %}
|
||||
{% include "status_codes.html" with label='returnOrderLineItem' options=ReturnOrderLineStatus.list %}
|
@ -24,8 +24,6 @@
|
||||
modalSetTitle,
|
||||
modalSubmit,
|
||||
openModal,
|
||||
printStockItemLabels,
|
||||
printTestReports,
|
||||
renderLink,
|
||||
scanItemsIntoLocation,
|
||||
showAlertDialog,
|
||||
@ -88,7 +86,7 @@ function serializeStockItem(pk, options={}) {
|
||||
|
||||
if (options.part) {
|
||||
// Work out the next available serial number
|
||||
inventreeGet(`/api/part/${options.part}/serial-numbers/`, {}, {
|
||||
inventreeGet(`{% url "api-part-list" %}${options.part}/serial-numbers/`, {}, {
|
||||
success: function(data) {
|
||||
if (data.next) {
|
||||
options.fields.serial_numbers.placeholder = `{% trans "Next available serial number" %}: ${data.next}`;
|
||||
@ -230,7 +228,7 @@ function stockItemFields(options={}) {
|
||||
enableFormInput('serial_numbers', opts);
|
||||
|
||||
// Request part serial number information from the server
|
||||
inventreeGet(`/api/part/${data.pk}/serial-numbers/`, {}, {
|
||||
inventreeGet(`{% url "api-part-list" %}${data.pk}/serial-numbers/`, {}, {
|
||||
success: function(data) {
|
||||
var placeholder = '';
|
||||
if (data.next) {
|
||||
@ -379,7 +377,7 @@ function duplicateStockItem(pk, options) {
|
||||
}
|
||||
|
||||
// First, we need the StockItem information
|
||||
inventreeGet(`/api/stock/${pk}/`, {}, {
|
||||
inventreeGet(`{% url "api-stock-list" %}${pk}/`, {}, {
|
||||
success: function(data) {
|
||||
|
||||
// Do not duplicate the serial number
|
||||
@ -656,8 +654,7 @@ function assignStockToCustomer(items, options={}) {
|
||||
|
||||
var buttons = `<div class='btn-group' role='group'>`;
|
||||
|
||||
buttons += makeIconButton(
|
||||
'fa-times icon-red',
|
||||
buttons += makeRemoveButton(
|
||||
'button-stock-item-remove',
|
||||
pk,
|
||||
'{% trans "Remove row" %}',
|
||||
@ -824,13 +821,13 @@ function mergeStockItems(items, options={}) {
|
||||
|
||||
quantity += stockStatusDisplay(item.status, {classes: 'float-right'});
|
||||
|
||||
var buttons = `<div class='btn-group' role='group'>`;
|
||||
|
||||
buttons += makeIconButton(
|
||||
'fa-times icon-red',
|
||||
'button-stock-item-remove',
|
||||
pk,
|
||||
'{% trans "Remove row" %}',
|
||||
let buttons = wrapButtons(
|
||||
makeIconButton(
|
||||
'fa-times icon-red',
|
||||
'button-stock-item-remove',
|
||||
pk,
|
||||
'{% trans "Remove row" %}',
|
||||
)
|
||||
);
|
||||
|
||||
html += `
|
||||
@ -1094,16 +1091,11 @@ function adjustStock(action, items, options={}) {
|
||||
);
|
||||
}
|
||||
|
||||
var buttons = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
buttons += makeIconButton(
|
||||
'fa-times icon-red',
|
||||
let buttons = wrapButtons(makeRemoveButton(
|
||||
'button-stock-item-remove',
|
||||
pk,
|
||||
'{% trans "Remove stock item" %}',
|
||||
);
|
||||
|
||||
buttons += `</div>`;
|
||||
));
|
||||
|
||||
html += `
|
||||
<tr id='stock_item_${pk}' class='stock-item-row'>
|
||||
@ -1341,18 +1333,11 @@ function loadStockTestResultsTable(table, options) {
|
||||
|
||||
var filterKey = options.filterKey || options.name || 'stocktests';
|
||||
|
||||
var filters = loadTableFilters(filterKey);
|
||||
|
||||
var params = {
|
||||
let params = {
|
||||
part: options.part,
|
||||
};
|
||||
|
||||
var original = {};
|
||||
|
||||
for (var k in params) {
|
||||
original[k] = params[k];
|
||||
filters[k] = params[k];
|
||||
}
|
||||
var filters = loadTableFilters(filterKey, params);
|
||||
|
||||
setupFilterList(filterKey, table, filterTarget);
|
||||
|
||||
@ -1360,7 +1345,7 @@ function loadStockTestResultsTable(table, options) {
|
||||
|
||||
// Helper function for rendering buttons
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
let html = '';
|
||||
|
||||
if (row.requires_attachment == false && row.requires_value == false && !row.result) {
|
||||
// Enable a "quick tick" option for this test result
|
||||
@ -1371,13 +1356,11 @@ function loadStockTestResultsTable(table, options) {
|
||||
|
||||
if (!grouped && row.result != null) {
|
||||
var pk = row.pk;
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-test-edit', pk, '{% trans "Edit test result" %}');
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-test-delete', pk, '{% trans "Delete test result" %}');
|
||||
html += makeEditButton('button-test-edit', pk, '{% trans "Edit test result" %}');
|
||||
html += makeDeleteButton('button-test-delete', pk, '{% trans "Delete test result" %}');
|
||||
}
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
}
|
||||
|
||||
var parent_node = 'parent node';
|
||||
@ -1396,7 +1379,7 @@ function loadStockTestResultsTable(table, options) {
|
||||
return '{% trans "No test results found" %}';
|
||||
},
|
||||
queryParams: filters,
|
||||
original: original,
|
||||
original: params,
|
||||
onPostBody: function() {
|
||||
table.treegrid({
|
||||
treeColumn: 0,
|
||||
@ -1444,7 +1427,7 @@ function loadStockTestResultsTable(table, options) {
|
||||
var html = value;
|
||||
|
||||
if (row.attachment) {
|
||||
var text = `<span class='fas fa-file-alt float-right'></span>`;
|
||||
let text = makeIconBadge('fa-file-alt', '');
|
||||
html += renderLink(text, row.attachment, {download: true});
|
||||
}
|
||||
|
||||
@ -1700,65 +1683,50 @@ function locationDetail(row, showLink=true) {
|
||||
}
|
||||
|
||||
|
||||
/* Load data into a stock table with adjustable options.
|
||||
* Fetches data (via AJAX) and loads into a bootstrap table.
|
||||
* Also links in default button callbacks.
|
||||
*
|
||||
* Options:
|
||||
* url - URL for the stock query
|
||||
* params - query params for augmenting stock data request
|
||||
* buttons - Which buttons to link to stock selection callbacks
|
||||
* filterList - <ul> element where filters are displayed
|
||||
* disableFilters: If true, disable custom filters
|
||||
*/
|
||||
function loadStockTable(table, options) {
|
||||
/* Load data into a stock table with adjustable options.
|
||||
* Fetches data (via AJAX) and loads into a bootstrap table.
|
||||
* Also links in default button callbacks.
|
||||
*
|
||||
* Options:
|
||||
* url - URL for the stock query
|
||||
* params - query params for augmenting stock data request
|
||||
* groupByField - Column for grouping stock items
|
||||
* buttons - Which buttons to link to stock selection callbacks
|
||||
* filterList - <ul> element where filters are displayed
|
||||
* disableFilters: If true, disable custom filters
|
||||
*/
|
||||
|
||||
// List of user-params which override the default filters
|
||||
|
||||
options.params['location_detail'] = true;
|
||||
options.params['part_detail'] = true;
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
var filterTarget = options.filterTarget || '#filter-list-stock';
|
||||
const filterTarget = options.filterTarget || '#filter-list-stock';
|
||||
|
||||
var filters = {};
|
||||
const filterKey = options.filterKey || options.name || 'stock';
|
||||
|
||||
var filterKey = options.filterKey || options.name || 'stock';
|
||||
let filters = loadTableFilters(filterKey, params);
|
||||
|
||||
if (!options.disableFilters) {
|
||||
filters = loadTableFilters(filterKey);
|
||||
}
|
||||
|
||||
var original = {};
|
||||
|
||||
for (var k in params) {
|
||||
original[k] = params[k];
|
||||
}
|
||||
|
||||
setupFilterList(filterKey, table, filterTarget, {download: true});
|
||||
setupFilterList(filterKey, table, filterTarget, {
|
||||
download: true,
|
||||
report: {
|
||||
url: '{% url "api-stockitem-testreport-list" %}',
|
||||
key: 'item',
|
||||
},
|
||||
labels: {
|
||||
url: '{% url "api-stockitem-label-list" %}',
|
||||
key: 'item',
|
||||
}
|
||||
});
|
||||
|
||||
// Override the default values, or add new ones
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
var grouping = true;
|
||||
|
||||
if ('grouping' in options) {
|
||||
grouping = options.grouping;
|
||||
}
|
||||
|
||||
var col = null;
|
||||
|
||||
// Explicitly disable part grouping functionality
|
||||
// Might be able to add this in later on,
|
||||
// but there is a bug which makes this crash if paginating on the server side.
|
||||
// Ref: https://github.com/wenzhixin/bootstrap-table/issues/3250
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
grouping = false;
|
||||
|
||||
var columns = [
|
||||
{
|
||||
checkbox: true,
|
||||
@ -2175,14 +2143,13 @@ function loadStockTable(table, options) {
|
||||
queryParams: filters,
|
||||
sidePagination: 'server',
|
||||
name: 'stock',
|
||||
original: original,
|
||||
original: params,
|
||||
showColumns: true,
|
||||
showFooter: true,
|
||||
columns: columns,
|
||||
});
|
||||
|
||||
var buttons = [
|
||||
'#stock-print-options',
|
||||
'#stock-options',
|
||||
];
|
||||
|
||||
@ -2206,31 +2173,6 @@ function loadStockTable(table, options) {
|
||||
}
|
||||
|
||||
// Automatically link button callbacks
|
||||
|
||||
$('#multi-item-print-label').click(function() {
|
||||
var selections = getTableData(table);
|
||||
|
||||
var items = [];
|
||||
|
||||
selections.forEach(function(item) {
|
||||
items.push(item.pk);
|
||||
});
|
||||
|
||||
printStockItemLabels(items);
|
||||
});
|
||||
|
||||
$('#multi-item-print-test-report').click(function() {
|
||||
var selections = getTableData(table);
|
||||
|
||||
var items = [];
|
||||
|
||||
selections.forEach(function(item) {
|
||||
items.push(item.pk);
|
||||
});
|
||||
|
||||
printTestReports(items);
|
||||
});
|
||||
|
||||
if (global_settings.BARCODE_ENABLE) {
|
||||
$('#multi-item-barcode-scan-into-location').click(function() {
|
||||
var selections = getTableData(table);
|
||||
@ -2420,21 +2362,17 @@ function loadStockLocationTable(table, options) {
|
||||
params.depth = global_settings.INVENTREE_TREE_DEPTH;
|
||||
}
|
||||
|
||||
var filters = {};
|
||||
|
||||
var filterKey = options.filterKey || options.name || 'location';
|
||||
|
||||
if (!options.disableFilters) {
|
||||
filters = loadTableFilters(filterKey);
|
||||
}
|
||||
let filters = loadTableFilters(filterKey, params);
|
||||
|
||||
var original = {};
|
||||
|
||||
for (var k in params) {
|
||||
original[k] = params[k];
|
||||
}
|
||||
|
||||
setupFilterList(filterKey, table, filterListElement, {download: true});
|
||||
setupFilterList(filterKey, table, filterListElement, {
|
||||
download: true,
|
||||
labels: {
|
||||
url: '{% url "api-stocklocation-label-list" %}',
|
||||
key: 'location'
|
||||
}
|
||||
});
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
@ -2484,7 +2422,7 @@ function loadStockLocationTable(table, options) {
|
||||
url: options.url || '{% url "api-location-list" %}',
|
||||
queryParams: filters,
|
||||
name: 'location',
|
||||
original: original,
|
||||
original: params,
|
||||
sortable: true,
|
||||
showColumns: true,
|
||||
onPostBody: function() {
|
||||
@ -2654,26 +2592,20 @@ function loadStockLocationTable(table, options) {
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Load stock history / tracking table for a given StockItem
|
||||
*/
|
||||
function loadStockTrackingTable(table, options) {
|
||||
|
||||
var cols = [];
|
||||
|
||||
var filterTarget = '#filter-list-stocktracking';
|
||||
const filterKey = 'stocktracking';
|
||||
|
||||
var filterKey = 'stocktracking';
|
||||
let params = options.params || {};
|
||||
|
||||
var filters = loadTableFilters(filterKey);
|
||||
let filters = loadTableFilters(filterKey, params);
|
||||
|
||||
var params = options.params;
|
||||
|
||||
var original = {};
|
||||
|
||||
for (var k in params) {
|
||||
original[k] = params[k];
|
||||
filters[k] = params[k];
|
||||
}
|
||||
|
||||
setupFilterList(filterKey, table, filterTarget);
|
||||
setupFilterList(filterKey, table, '#filter-list-stocktracking');
|
||||
|
||||
// Date
|
||||
cols.push({
|
||||
@ -2747,10 +2679,10 @@ function loadStockTrackingTable(table, options) {
|
||||
html += '</td></tr>';
|
||||
}
|
||||
|
||||
// Purchase Order Information
|
||||
// PurchaseOrder Information
|
||||
if (details.purchaseorder) {
|
||||
|
||||
html += `<tr><th>{% trans "Purchase Order" %}</td>`;
|
||||
html += `<tr><th>{% trans "Purchase Order" %}</th>`;
|
||||
|
||||
html += '<td>';
|
||||
|
||||
@ -2766,6 +2698,40 @@ function loadStockTrackingTable(table, options) {
|
||||
html += '</td></tr>';
|
||||
}
|
||||
|
||||
// SalesOrder information
|
||||
if (details.salesorder) {
|
||||
html += `<tr><th>{% trans "Sales Order" %}</th>`;
|
||||
html += '<td>';
|
||||
|
||||
if (details.salesorder_detail) {
|
||||
html += renderLink(
|
||||
details.salesorder_detail.reference,
|
||||
`/order/sales-order/${details.salesorder}`
|
||||
);
|
||||
} else {
|
||||
html += `<em>{% trans "Sales Order no longer exists" %}</em>`;
|
||||
}
|
||||
|
||||
html += `</td></tr>`;
|
||||
}
|
||||
|
||||
// ReturnOrder information
|
||||
if (details.returnorder) {
|
||||
html += `<tr><th>{% trans "Return Order" %}</th>`;
|
||||
html += '<td>';
|
||||
|
||||
if (details.returnorder_detail) {
|
||||
html += renderLink(
|
||||
details.returnorder_detail.reference,
|
||||
`/order/return-order/${details.returnorder}/`
|
||||
);
|
||||
} else {
|
||||
html += `<em>{% trans "Return Order no longer exists" %}</em>`;
|
||||
}
|
||||
|
||||
html += `</td></tr>`;
|
||||
}
|
||||
|
||||
// Customer information
|
||||
if (details.customer) {
|
||||
|
||||
@ -2808,12 +2774,7 @@ function loadStockTrackingTable(table, options) {
|
||||
html += `<tr><th>{% trans "Status" %}</td>`;
|
||||
|
||||
html += '<td>';
|
||||
html += stockStatusDisplay(
|
||||
details.status,
|
||||
{
|
||||
classes: 'float-right',
|
||||
}
|
||||
);
|
||||
html += stockStatusDisplay(details.status);
|
||||
html += '</td></tr>';
|
||||
|
||||
}
|
||||
@ -2865,7 +2826,7 @@ function loadStockTrackingTable(table, options) {
|
||||
table.inventreeTable({
|
||||
method: 'get',
|
||||
queryParams: filters,
|
||||
original: original,
|
||||
original: params,
|
||||
columns: cols,
|
||||
url: options.url,
|
||||
});
|
||||
@ -2951,14 +2912,12 @@ function loadInstalledInTable(table, options) {
|
||||
title: '',
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
var pk = row.pk;
|
||||
var html = '';
|
||||
let pk = row.pk;
|
||||
let html = '';
|
||||
|
||||
html += `<div class='btn-group float-right' role='group'>`;
|
||||
html += makeIconButton('fa-unlink', 'button-uninstall', pk, '{% trans "Uninstall Stock Item" %}');
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
return wrapButtons(html);
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -2,23 +2,15 @@
|
||||
{% load status_codes %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% include "status_codes.html" with label='stock' options=StockStatus.list %}
|
||||
{% include "status_codes.html" with label='stockHistory' options=StockHistoryCode.list %}
|
||||
{% include "status_codes.html" with label='build' options=BuildStatus.list %}
|
||||
{% include "status_codes.html" with label='purchaseOrder' options=PurchaseOrderStatus.list %}
|
||||
{% include "status_codes.html" with label='salesOrder' options=SalesOrderStatus.list %}
|
||||
|
||||
/* globals
|
||||
global_settings
|
||||
purchaseOrderCodes,
|
||||
returnOrderCodes,
|
||||
salesOrderCodes,
|
||||
*/
|
||||
|
||||
/* exported
|
||||
buildStatusDisplay,
|
||||
getAvailableTableFilters,
|
||||
purchaseOrderStatusDisplay,
|
||||
salesOrderStatusDisplay,
|
||||
stockHistoryStatusDisplay,
|
||||
stockStatusDisplay,
|
||||
*/
|
||||
|
||||
|
||||
@ -26,6 +18,42 @@ function getAvailableTableFilters(tableKey) {
|
||||
|
||||
tableKey = tableKey.toLowerCase();
|
||||
|
||||
// Filters for "returnorder" table
|
||||
if (tableKey == 'returnorder') {
|
||||
return {
|
||||
status: {
|
||||
title: '{% trans "Order status" %}',
|
||||
options: returnOrderCodes
|
||||
},
|
||||
outstanding: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Outstanding" %}',
|
||||
},
|
||||
overdue: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Overdue" %}',
|
||||
},
|
||||
assigned_to_me: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assigned to me" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for "returnorderlineitem" table
|
||||
if (tableKey == 'returnorderlineitem') {
|
||||
return {
|
||||
received: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Received" %}',
|
||||
},
|
||||
outcome: {
|
||||
title: '{% trans "Outcome" %}',
|
||||
options: returnOrderLineItemCodes,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for "variant" table
|
||||
if (tableKey == 'variants') {
|
||||
return {
|
||||
@ -363,7 +391,7 @@ function getAvailableTableFilters(tableKey) {
|
||||
title: '{% trans "Responsible" %}',
|
||||
options: function() {
|
||||
var ownersList = {};
|
||||
inventreeGet(`/api/user/owner/`, {}, {
|
||||
inventreeGet('{% url "api-owner-list" %}', {}, {
|
||||
async: false,
|
||||
success: function(response) {
|
||||
for (key in response) {
|
||||
@ -445,6 +473,10 @@ function getAvailableTableFilters(tableKey) {
|
||||
type: 'bool',
|
||||
title: '{% trans "Overdue" %}',
|
||||
},
|
||||
assigned_to_me: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assigned to me" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@
|
||||
customGroupSorter,
|
||||
downloadTableData,
|
||||
getTableData,
|
||||
reloadtable,
|
||||
reloadBootstrapTable,
|
||||
renderLink,
|
||||
reloadTableFilters,
|
||||
constructExpandCollapseButtons,
|
||||
@ -20,8 +20,23 @@
|
||||
* Reload a named table
|
||||
* @param table
|
||||
*/
|
||||
function reloadtable(table) {
|
||||
$(table).bootstrapTable('refresh');
|
||||
function reloadBootstrapTable(table) {
|
||||
|
||||
let tbl = table;
|
||||
|
||||
if (tbl) {
|
||||
if (typeof tbl === 'string' || tbl instanceof String) {
|
||||
tbl = $(tbl);
|
||||
}
|
||||
|
||||
if (tbl.exists()) {
|
||||
tbl.bootstrapTable('refresh');
|
||||
} else {
|
||||
console.error(`Invalid table name passed to reloadTable(): ${table}`);
|
||||
}
|
||||
} else {
|
||||
console.error(`Null value passed to reloadTable()`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -127,7 +142,7 @@ function constructExpandCollapseButtons(table, idx=0) {
|
||||
*/
|
||||
function getTableData(table, allowEmpty=false) {
|
||||
|
||||
var data = $(table).bootstrapTable('getSelections');
|
||||
let data = $(table).bootstrapTable('getSelections');
|
||||
|
||||
if (data.length == 0 && !allowEmpty) {
|
||||
data = $(table).bootstrapTable('getData');
|
||||
|
@ -47,18 +47,23 @@
|
||||
<ul class='dropdown-menu' aria-labelledby="buyMenuDropdown">
|
||||
<li><a class='dropdown-item' href="{% url 'supplier-index' %}"><span class='fas fa-building icon-header'></span>{% trans "Suppliers" %}</a></li>
|
||||
<li><a class='dropdown-item' href="{% url 'manufacturer-index' %}"><span class='fas fa-industry icon-header'></span>{% trans "Manufacturers" %}</a></li>
|
||||
<li><a class='dropdown-item' href="{% url 'po-index' %}"><span class='fas fa-list icon-header'></span>{% trans "Purchase Orders" %}</a></li>
|
||||
<li><a class='dropdown-item' href="{% url 'purchase-order-index' %}"><span class='fas fa-list icon-header'></span>{% trans "Purchase Orders" %}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if roles.sales_order.view %}
|
||||
{% if roles.sales_order.view or roles.return_order.view %}
|
||||
<li class='nav-item dropdown'>
|
||||
<a class='nav-link dropdown-toggle' href='#' id='sellMenuDropdown' role='button' data-bs-toggle='dropdown'>
|
||||
<span class='fas fa-truck icon-header'></span>{% trans "Sell" %}
|
||||
</a>
|
||||
<ul class='dropdown-menu'>
|
||||
<li><a class='dropdown-item' href="{% url 'customer-index' %}"><span class='fas fa-user-tie icon-header'></span>{% trans "Customers" %}</a>
|
||||
<li><a class='dropdown-item' href="{% url 'so-index' %}"><span class='fas fa-list icon-header'></span>{% trans "Sales Orders" %}</a></li>
|
||||
{% if roles.sales_order.view %}
|
||||
<li><a class='dropdown-item' href="{% url 'sales-order-index' %}"><span class='fas fa-list icon-header'></span>{% trans "Sales Orders" %}</a></li>
|
||||
{% endif %}
|
||||
{% if roles.return_order.view and return_order_enabled %}
|
||||
<li><a class='dropdown-item' href="{% url 'return-order-index' %}"><span class='fas fa-undo icon-header'></span>{% trans "Return Orders" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
@ -22,18 +22,6 @@
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Printing actions menu -->
|
||||
<div class='btn-group'>
|
||||
<button id='stock-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='multi-item-print-label' title='{% trans "Print labels" %}'><span class='fas fa-tags'></span> {% trans "Print labels" %}</a></li>
|
||||
{% if test_report_enabled %}
|
||||
<li><a class='dropdown-item' href='#' id='multi-item-print-test-report' title='{% trans "Print test reports" %}'><span class='fas fa-file-pdf'></span> {% trans "Print test reports" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% if not read_only %}
|
||||
{% if roles.stock.change or roles.stock.delete %}
|
||||
<div class="btn-group" role="group">
|
||||
|
Reference in New Issue
Block a user