mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 13:05:42 +00:00
Merge pull request #1716 from SchrodingersGat/drf-api-forms
[WIP] API Forms
This commit is contained in:
@ -10,35 +10,6 @@
|
||||
|
||||
<div class='dropzone' id='attachment-dropzone'>
|
||||
<table class='table table-striped table-condensed' data-toolbar='#attachment-buttons' id='attachment-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th data-field='file' data-sortable='true' data-searchable='true'>{% trans "File" %}</th>
|
||||
<th data-field='comment' data-sortable='true' data-searchable='true'>{% trans "Comment" %}</th>
|
||||
<th data-field='user' data-sortable='true' data-searchable='true'>{% trans "Uploaded" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for attachment in attachments %}
|
||||
<tr>
|
||||
<td><a href='/media/{{ attachment.attachment }}'>{{ attachment.basename }}</a></td>
|
||||
<td>{{ attachment.comment }}</td>
|
||||
<td>
|
||||
{% if attachment.upload_date %}{{ attachment.upload_date }}{% endif %}
|
||||
{% if attachment.user %}<span class='badge'>{{ attachment.user.username }}</div>{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class='btn-group' style='float: right;'>
|
||||
<button type='button' class='btn btn-default btn-glyph attachment-edit-button' pk="{{ attachment.id }}" data-toggle='tooltip' title='{% trans "Edit attachment" %}'>
|
||||
<span class='fas fa-edit icon-blue'/>
|
||||
</button>
|
||||
<button type='button' class='btn btn-default btn-glyph attachment-delete-button' pk="{{ attachment.id }}" data-toggle='tooltip' title='{% trans "Delete attachment" %}'>
|
||||
<span class='fas fa-trash-alt icon-red'/>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
</table>
|
||||
</div>
|
@ -40,8 +40,8 @@
|
||||
<link rel="stylesheet" href="{% static 'bootstrap-table/bootstrap-table.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/select2.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/select2-bootstrap.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'select2/css/select2.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'select2/css/select2-bootstrap.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/bootstrap-toggle.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'fullcalendar/main.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'script/jquery-ui/jquery-ui.min.css' %}">
|
||||
@ -136,7 +136,7 @@
|
||||
<!-- 3rd party general js -->
|
||||
<script type="text/javascript" src="{% static 'fullcalendar/main.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'fullcalendar/locales-all.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'script/select2/select2.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'select2/js/select2.full.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/moment.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/chart.min.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/clipboard.min.js' %}"></script>
|
||||
@ -144,11 +144,14 @@
|
||||
|
||||
<!-- general InvenTree -->
|
||||
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/api.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/notification.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/sidenav.js' %}"></script>
|
||||
|
||||
<!-- translated -->
|
||||
<script type='text/javascript' src="{% i18n_static 'api.js' %}"></script>
|
||||
<script type='text/javascript' src="{% i18n_static 'attachment.js' %}"></script>
|
||||
<script type='text/javascript' src="{% i18n_static 'forms.js' %}"></script>
|
||||
<script type='text/javascript' src="{% i18n_static 'model_renderers.js' %}"></script>
|
||||
<script type='text/javascript' src="{% i18n_static 'barcode.js' %}"></script>
|
||||
<script type='text/javascript' src="{% i18n_static 'bom.js' %}"></script>
|
||||
<script type='text/javascript' src="{% i18n_static 'company.js' %}"></script>
|
||||
|
141
InvenTree/templates/js/api.js
Normal file
141
InvenTree/templates/js/api.js
Normal file
@ -0,0 +1,141 @@
|
||||
var jQuery = window.$;
|
||||
|
||||
// using jQuery
|
||||
function getCookie(name) {
|
||||
var cookieValue = null;
|
||||
if (document.cookie && document.cookie != '') {
|
||||
var cookies = document.cookie.split(';');
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
var cookie = jQuery.trim(cookies[i]);
|
||||
// Does this cookie string begin with the name we want?
|
||||
if (cookie.substring(0, name.length + 1) == (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
function inventreeGet(url, filters={}, options={}) {
|
||||
|
||||
// Middleware token required for data update
|
||||
//var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
|
||||
var csrftoken = getCookie('csrftoken');
|
||||
|
||||
return $.ajax({
|
||||
beforeSend: function(xhr, settings) {
|
||||
xhr.setRequestHeader('X-CSRFToken', csrftoken);
|
||||
},
|
||||
url: url,
|
||||
type: 'GET',
|
||||
data: filters,
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
success: function(response) {
|
||||
if (options.success) {
|
||||
options.success(response);
|
||||
}
|
||||
},
|
||||
error: function(xhr, ajaxOptions, thrownError) {
|
||||
console.error('Error on GET at ' + url);
|
||||
console.error(thrownError);
|
||||
if (options.error) {
|
||||
options.error({
|
||||
error: thrownError
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
// CSRF cookie token
|
||||
var csrftoken = getCookie('csrftoken');
|
||||
|
||||
return $.ajax({
|
||||
beforeSend: function(xhr, settings) {
|
||||
xhr.setRequestHeader('X-CSRFToken', csrftoken);
|
||||
},
|
||||
url: url,
|
||||
method: options.method || 'POST',
|
||||
data: data,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(data, status, xhr) {
|
||||
if (options.success) {
|
||||
options.success(data, status, xhr);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.log('Form data upload failure: ' + status);
|
||||
|
||||
if (options.error) {
|
||||
options.error(xhr, status, error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function inventreePut(url, data={}, options={}) {
|
||||
|
||||
var method = options.method || 'PUT';
|
||||
|
||||
// Middleware token required for data update
|
||||
//var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
|
||||
var csrftoken = getCookie('csrftoken');
|
||||
|
||||
return $.ajax({
|
||||
beforeSend: function(xhr, settings) {
|
||||
xhr.setRequestHeader('X-CSRFToken', csrftoken);
|
||||
},
|
||||
url: url,
|
||||
type: method,
|
||||
data: JSON.stringify(data),
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
success: function(response, status) {
|
||||
if (options.success) {
|
||||
options.success(response, status);
|
||||
}
|
||||
if (options.reloadOnSuccess) {
|
||||
location.reload();
|
||||
}
|
||||
},
|
||||
error: function(xhr, ajaxOptions, thrownError) {
|
||||
if (options.error) {
|
||||
options.error(xhr, ajaxOptions, thrownError);
|
||||
} else {
|
||||
console.error(`Error on ${method} to '${url}' - STATUS ${xhr.status}`);
|
||||
console.error(thrownError);
|
||||
}
|
||||
},
|
||||
complete: function(xhr, status) {
|
||||
if (options.complete) {
|
||||
options.complete(xhr, status);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function inventreeDelete(url, options={}) {
|
||||
/*
|
||||
* Delete a record
|
||||
*/
|
||||
|
||||
options = options || {};
|
||||
|
||||
options.method = 'DELETE';
|
||||
|
||||
inventreePut(url, {}, options);
|
||||
|
||||
}
|
86
InvenTree/templates/js/attachment.js
Normal file
86
InvenTree/templates/js/attachment.js
Normal file
@ -0,0 +1,86 @@
|
||||
{% load i18n %}
|
||||
|
||||
function reloadAttachmentTable() {
|
||||
|
||||
$('#attachment-table').bootstrapTable("refresh");
|
||||
}
|
||||
|
||||
|
||||
function loadAttachmentTable(url, options) {
|
||||
|
||||
var table = options.table || '#attachment-table';
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: url,
|
||||
name: options.name || 'attachments',
|
||||
formatNoMatches: function() { return '{% trans "No attachments found" %}'},
|
||||
sortable: true,
|
||||
search: false,
|
||||
queryParams: options.filters || {},
|
||||
onPostBody: function() {
|
||||
// Add callback for 'edit' button
|
||||
$(table).find('.button-attachment-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
if (options.onEdit) {
|
||||
options.onEdit(pk);
|
||||
}
|
||||
});
|
||||
|
||||
// Add callback for 'delete' button
|
||||
$(table).find('.button-attachment-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
if (options.onDelete) {
|
||||
options.onDelete(pk);
|
||||
}
|
||||
});
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'attachment',
|
||||
title: '{% trans "File" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
var split = value.split('/');
|
||||
|
||||
return renderLink(split[split.length - 1], value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'comment',
|
||||
title: '{% trans "Comment" %}',
|
||||
},
|
||||
{
|
||||
field: 'upload_date',
|
||||
title: '{% trans "Upload Date" %}',
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
formatter: function(value, row) {
|
||||
var html = '';
|
||||
|
||||
html = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
html += makeIconButton(
|
||||
'fa-edit icon-blue',
|
||||
'button-attachment-edit',
|
||||
row.pk,
|
||||
'{% trans "Edit attachment" %}',
|
||||
);
|
||||
|
||||
html += makeIconButton(
|
||||
'fa-trash-alt icon-red',
|
||||
'button-attachment-delete',
|
||||
row.pk,
|
||||
'{% trans "Delete attachment" %}',
|
||||
);
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
@ -514,14 +514,13 @@ function loadBomTable(table, options) {
|
||||
var pk = $(this).attr('pk');
|
||||
var url = `/part/bom/${pk}/delete/`;
|
||||
|
||||
launchModalForm(
|
||||
url,
|
||||
{
|
||||
success: function() {
|
||||
reloadBomTable(table);
|
||||
}
|
||||
constructForm(`/api/bom/${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete BOM Item" %}',
|
||||
onSuccess: function() {
|
||||
reloadBomTable(table);
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
table.on('click', '.bom-edit-button', function() {
|
||||
|
@ -1,5 +1,74 @@
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
// Returns a default form-set for creating / editing a Company object
|
||||
function companyFormFields(options={}) {
|
||||
|
||||
return {
|
||||
name: {},
|
||||
description: {},
|
||||
website: {
|
||||
icon: 'fa-globe',
|
||||
},
|
||||
address: {
|
||||
icon: 'fa-envelope',
|
||||
},
|
||||
currency: {
|
||||
icon: 'fa-dollar-sign',
|
||||
},
|
||||
phone: {
|
||||
icon: 'fa-phone',
|
||||
},
|
||||
email: {
|
||||
icon: 'fa-at',
|
||||
},
|
||||
contact: {
|
||||
icon: 'fa-address-card',
|
||||
},
|
||||
is_supplier: {},
|
||||
is_manufacturer: {},
|
||||
is_customer: {}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function editCompany(pk, options={}) {
|
||||
|
||||
var fields = options.fields || companyFormFields();
|
||||
|
||||
constructForm(
|
||||
`/api/company/${pk}/`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
fields: fields,
|
||||
reload: true,
|
||||
title: '{% trans "Edit Company" %}',
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
* Launches a form to create a new company.
|
||||
* As this can be called from many different contexts,
|
||||
* we abstract it here!
|
||||
*/
|
||||
function createCompany(options={}) {
|
||||
|
||||
// Default field set
|
||||
var fields = options.fields || companyFormFields();
|
||||
|
||||
constructForm(
|
||||
'{% url "api-company-list" %}',
|
||||
{
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
follow: true,
|
||||
title: '{% trans "Add new Company" %}',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function loadCompanyTable(table, url, options={}) {
|
||||
/*
|
||||
* Load company listing data into specified table.
|
||||
@ -101,6 +170,61 @@ function loadCompanyTable(table, url, options={}) {
|
||||
}
|
||||
|
||||
|
||||
function deleteManufacturerParts(selections, options={}) {
|
||||
|
||||
if (selections.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parts = [];
|
||||
|
||||
var text = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
<p>{% trans "The following manufacturer parts will be deleted" %}:</p>
|
||||
<ul>`;
|
||||
|
||||
selections.forEach(function(item) {
|
||||
parts.push(item.pk);
|
||||
|
||||
text += `
|
||||
<li>
|
||||
<p>${item.MPN} - ${item.part_detail.full_name}</p>
|
||||
</li>`;
|
||||
});
|
||||
|
||||
text += `
|
||||
</ul>
|
||||
</div>`;
|
||||
|
||||
showQuestionDialog(
|
||||
'{% trans "Delete Manufacturer Parts" %}',
|
||||
text,
|
||||
{
|
||||
accept_text: '{% trans "Delete" %}',
|
||||
accept: function() {
|
||||
|
||||
// Delete each manufacturer part
|
||||
var requests = [];
|
||||
|
||||
parts.forEach(function(pk) {
|
||||
var url = `/api/company/part/manufacturer/${pk}`;
|
||||
|
||||
requests.push(inventreeDelete(url));
|
||||
});
|
||||
|
||||
// Wait for all the requests to complete
|
||||
$.when.apply($, requests).then(function() {
|
||||
|
||||
if (options.onSuccess) {
|
||||
options.onSuccess();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function loadManufacturerPartTable(table, url, options) {
|
||||
/*
|
||||
* Load manufacturer part table
|
||||
@ -228,7 +352,7 @@ function loadManufacturerPartParameterTable(table, url, options) {
|
||||
{
|
||||
checkbox: true,
|
||||
switchable: false,
|
||||
visible: false,
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
@ -273,27 +397,28 @@ function loadManufacturerPartParameterTable(table, url, options) {
|
||||
$(table).find('.button-parameter-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
launchModalForm(
|
||||
`/manufacturer-part/parameter/${pk}/edit/`,
|
||||
{
|
||||
success: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
constructForm(`/api/company/part/manufacturer/parameter/${pk}/`, {
|
||||
fields: {
|
||||
name: {},
|
||||
value: {},
|
||||
units: {},
|
||||
},
|
||||
title: '{% trans "Edit Parameter" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
);
|
||||
|
||||
});
|
||||
});
|
||||
$(table).find('.button-parameter-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
launchModalForm(
|
||||
`/manufacturer-part/parameter/${pk}/delete/`,
|
||||
{
|
||||
success: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
constructForm(`/api/company/part/manufacturer/parameter/${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Parameter" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
1629
InvenTree/templates/js/forms.js
Normal file
1629
InvenTree/templates/js/forms.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,114 @@
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
/*
|
||||
* Create and display a new modal dialog
|
||||
*
|
||||
* options:
|
||||
* - title: Form title to render
|
||||
* - submitText: Text to render on 'submit' button (default = "Submit")
|
||||
* - closeText: Text to render on 'close' button (default = "Cancel")
|
||||
* - focus: Name of field to focus on after launching
|
||||
*/
|
||||
function createNewModal(options={}) {
|
||||
|
||||
|
||||
var id = 1;
|
||||
|
||||
// Check out what modal forms are already being displayed
|
||||
$('.inventree-modal').each(function() {
|
||||
|
||||
var split = this.id.split('-');
|
||||
var modal_id = parseInt(split[2]);
|
||||
|
||||
if (modal_id >= id) {
|
||||
id = modal_id + 1;
|
||||
}
|
||||
});
|
||||
|
||||
var html = `
|
||||
<div class='modal fade modal-fixed-footer modal-primary inventree-modal' role='dialog' id='modal-form-${id}'>
|
||||
<div class='modal-dialog'>
|
||||
<div class='modal-content'>
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label='{% trans "Close" %}'>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
<h3 id='modal-title'>
|
||||
<!-- Form title to be injected here -->
|
||||
</h3>
|
||||
</div>
|
||||
<div class='modal-form-content-wrapper'>
|
||||
<div id='pre-form-content'>
|
||||
<!-- Content can be inserted here *before* the form fields -->
|
||||
</div>
|
||||
<div id='non-field-errors'>
|
||||
<!-- Form error messages go here -->
|
||||
</div>
|
||||
<div id='form-content' class='modal-form-content'>
|
||||
<!-- Form content will be injected here-->
|
||||
</div>
|
||||
<div id='post-form-content'>
|
||||
<!-- Content can be inserted here *after* the form fields -->
|
||||
</div>
|
||||
</div>
|
||||
<div class='modal-footer'>
|
||||
<div id='modal-footer-buttons'>
|
||||
<!-- Extra buttons can be inserted here -->
|
||||
</div>
|
||||
<button type='button' class='btn btn-default' id='modal-form-close' data-dismiss='modal'>{% trans "Cancel" %}</button>
|
||||
<button type='button' class='btn btn-primary' id='modal-form-submit'>{% trans "Submit" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
$('body').append(html);
|
||||
|
||||
var modal_name = `#modal-form-${id}`;
|
||||
|
||||
$(modal_name).on('shown.bs.modal', function() {
|
||||
$(modal_name + ' .modal-form-content').scrollTop(0);
|
||||
|
||||
if (options.focus) {
|
||||
getFieldByName(modal_name, options.focus).focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Automatically remove the modal when it is deleted!
|
||||
$(modal_name).on('hidden.bs.modal', function(e) {
|
||||
$(modal_name).remove();
|
||||
});
|
||||
|
||||
// Capture "enter" key input
|
||||
$(modal_name).on('keydown', 'input', function(event) {
|
||||
|
||||
|
||||
if (event.keyCode == 13) {
|
||||
event.preventDefault();
|
||||
// Simulate a click on the 'Submit' button
|
||||
$(modal_name).find("#modal-form-submit").click();
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$(modal_name).modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false,
|
||||
});
|
||||
|
||||
// Set labels based on supplied options
|
||||
modalSetTitle(modal_name, options.title || '{% trans "Form Title" %}');
|
||||
modalSetSubmitText(modal_name, options.submitText || '{% trans "Submit" %}');
|
||||
modalSetCloseText(modal_name, options.cancelText || '{% trans "Cancel" %}');
|
||||
|
||||
// Return the "name" of the modal
|
||||
return modal_name;
|
||||
}
|
||||
|
||||
|
||||
function makeOption(text, value, title) {
|
||||
/* Format an option for a select element
|
||||
*/
|
||||
@ -991,8 +1100,6 @@ function hideModalImage() {
|
||||
function showModalImage(image_url) {
|
||||
// Display full-screen modal image
|
||||
|
||||
console.log('showing modal image: ' + image_url);
|
||||
|
||||
var modal = $('#modal-image-dialog');
|
||||
|
||||
// Set image content
|
||||
|
155
InvenTree/templates/js/model_renderers.js
Normal file
155
InvenTree/templates/js/model_renderers.js
Normal file
@ -0,0 +1,155 @@
|
||||
{% load i18n %}
|
||||
|
||||
/*
|
||||
* This file contains functions for rendering various InvenTree database models,
|
||||
* in particular for displaying them in modal forms in a 'select2' context.
|
||||
*
|
||||
* Each renderer is provided with three arguments:
|
||||
*
|
||||
* - name: The 'name' of the model instance in the referring model
|
||||
* - data: JSON data which represents the model instance. Returned via a GET request.
|
||||
* - parameters: The field parameters provided via an OPTIONS request to the endpoint.
|
||||
* - options: User options provided by the client
|
||||
*/
|
||||
|
||||
|
||||
// Renderer for "Company" model
|
||||
function renderCompany(name, data, parameters, options) {
|
||||
|
||||
var html = `<span>${data.name}</span> - <i>${data.description}</i>`;
|
||||
|
||||
html += `<span class='float-right'>{% trans "Company ID" %}: ${data.pk}</span>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "StockItem" model
|
||||
function renderStockItem(name, data, parameters, options) {
|
||||
|
||||
var image = data.part_detail.thumbnail || data.part_detail.image;
|
||||
|
||||
if (!image) {
|
||||
image = `/static/img/blank_image.png`;
|
||||
}
|
||||
|
||||
var html = `<img src='${image}' class='select2-thumbnail'>`;
|
||||
|
||||
html += ` <span>${data.part_detail.full_name || data.part_detail.name}</span>`;
|
||||
|
||||
if (data.serial && data.quantity == 1) {
|
||||
html += ` - <i>{% trans "Serial Number" %}: ${data.serial}`;
|
||||
} else {
|
||||
html += ` - <i>{% trans "Quantity" %}: ${data.quantity}`;
|
||||
}
|
||||
|
||||
if (data.part_detail.description) {
|
||||
html += `<p><small>${data.part_detail.description}</small></p>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "StockLocation" model
|
||||
function renderStockLocation(name, data, parameters, options) {
|
||||
|
||||
var html = `<span>${data.name}</span>`;
|
||||
|
||||
if (data.description) {
|
||||
html += ` - <i>${data.description}</i>`;
|
||||
}
|
||||
|
||||
html += `<span class='float-right'>{% trans "Location ID" %}: ${data.pk}</span>`;
|
||||
|
||||
if (data.pathstring) {
|
||||
html += `<p><small>${data.pathstring}</small></p>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "Part" model
|
||||
function renderPart(name, data, parameters, options) {
|
||||
|
||||
var image = data.image;
|
||||
|
||||
if (!image) {
|
||||
image = `/static/img/blank_image.png`;
|
||||
}
|
||||
|
||||
var html = `<img src='${image}' class='select2-thumbnail'>`;
|
||||
|
||||
html += ` <span>${data.full_name || data.name}</span>`;
|
||||
|
||||
if (data.description) {
|
||||
html += ` - <i>${data.description}</i>`;
|
||||
}
|
||||
|
||||
html += `<span class='float-right'>{% trans "Part ID" %}: ${data.pk}</span>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "Owner" model
|
||||
function renderOwner(name, data, parameters, options) {
|
||||
|
||||
var html = `<span>${data.name}</span>`;
|
||||
|
||||
|
||||
switch (data.label) {
|
||||
case 'user':
|
||||
html += `<span class='float-right fas fa-user'></span>`;
|
||||
break;
|
||||
case 'group':
|
||||
html += `<span class='float-right fas fa-users'></span>`;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "PartCategory" model
|
||||
function renderPartCategory(name, data, parameters, options) {
|
||||
|
||||
var html = `<span><b>${data.name}</b></span>`;
|
||||
|
||||
if (data.description) {
|
||||
html += ` - <i>${data.description}</i>`;
|
||||
}
|
||||
|
||||
html += `<span class='float-right'>{% trans "Category ID" %}: ${data.pk}</span>`;
|
||||
|
||||
if (data.pathstring) {
|
||||
html += `<p><small>${data.pathstring}</small></p>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Rendered for "SupplierPart" model
|
||||
function renderSupplierPart(name, data, parameters, options) {
|
||||
|
||||
var image = data.supplier_detail.image;
|
||||
|
||||
if (!image) {
|
||||
image = `/static/img/blank_image.png`;
|
||||
}
|
||||
|
||||
var html = `<img src='${image}' class='select2-thumbnail'>`;
|
||||
|
||||
html += ` <span><b>${data.supplier_detail.name}</b> - ${data.SKU}</span>`;
|
||||
html += ` - <i>${data.part_detail.full_name}</i>`;
|
||||
|
||||
html += `<span class='float-right'>{% trans "Supplier Part ID" %}: ${data.pk}</span>`;
|
||||
|
||||
|
||||
return html;
|
||||
|
||||
}
|
@ -1,6 +1,68 @@
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
|
||||
// Create a new SalesOrder
|
||||
function createSalesOrder(options={}) {
|
||||
|
||||
constructForm('{% url "api-so-list" %}', {
|
||||
method: 'POST',
|
||||
fields: {
|
||||
reference: {
|
||||
prefix: '{% settings_value "SALESORDER_REFERENCE_PREFIX" %}',
|
||||
},
|
||||
customer: {
|
||||
value: options.customer,
|
||||
},
|
||||
description: {},
|
||||
target_date: {
|
||||
icon: 'fa-calendar-alt',
|
||||
},
|
||||
link: {
|
||||
icon: 'fa-link',
|
||||
},
|
||||
responsible: {
|
||||
icon: 'fa-user',
|
||||
}
|
||||
},
|
||||
onSuccess: function(data) {
|
||||
location.href = `/order/sales-order/${data.pk}/`;
|
||||
},
|
||||
title: '{% trans "Create Sales Order" %}',
|
||||
});
|
||||
}
|
||||
|
||||
// Create a new PurchaseOrder
|
||||
function createPurchaseOrder(options={}) {
|
||||
|
||||
constructForm('{% url "api-po-list" %}', {
|
||||
method: 'POST',
|
||||
fields: {
|
||||
reference: {
|
||||
prefix: "{% settings_value 'PURCHASEORDER_REFERENCE_PREFIX' %}",
|
||||
},
|
||||
supplier: {
|
||||
value: options.supplier,
|
||||
},
|
||||
description: {},
|
||||
target_date: {
|
||||
icon: 'fa-calendar-alt',
|
||||
},
|
||||
link: {
|
||||
icon: 'fa-link',
|
||||
},
|
||||
responsible: {
|
||||
icon: 'fa-user',
|
||||
}
|
||||
},
|
||||
onSuccess: function(data) {
|
||||
location.href = `/order/purchase-order/${data.pk}/`;
|
||||
},
|
||||
title: '{% trans "Create Purchase Order" %}',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function removeOrderRowFromOrderWizard(e) {
|
||||
/* Remove a part selection from an order form. */
|
||||
|
||||
@ -266,6 +328,11 @@ function loadSalesOrderTable(table, options) {
|
||||
field: 'customer_detail',
|
||||
title: '{% trans "Customer" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
if (!row.customer_detail) {
|
||||
return '{% trans "Invalid Customer" %}';
|
||||
}
|
||||
|
||||
return imageHoverIcon(row.customer_detail.image) + renderLink(row.customer_detail.name, `/company/${row.customer}/sales-orders/`);
|
||||
}
|
||||
},
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
</div>
|
||||
|
||||
<div class='modal fade modal-fixed-footer modal-primary' tabindex='-1' role='dialog' id='modal-form'>
|
||||
<div class='modal fade modal-fixed-footer modal-primary' role='dialog' id='modal-form'>
|
||||
<div class='modal-dialog'>
|
||||
<div class='modal-content'>
|
||||
<div class="modal-header">
|
||||
@ -33,7 +33,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='modal fade modal-fixed-footer modal-secondary' tabindex='-1' role='dialog' id='modal-form-secondary'>
|
||||
<div class='modal fade modal-fixed-footer modal-secondary' role='dialog' id='modal-form-secondary'>
|
||||
<div class='modal-dialog'>
|
||||
<div class='modal-content'>
|
||||
<div class="modal-header">
|
||||
@ -59,7 +59,7 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class='modal fade modal-fixed-footer' tabindex='-1' role='dialog' id='modal-question-dialog'>
|
||||
<div class='modal fade modal-fixed-footer' role='dialog' id='modal-question-dialog'>
|
||||
<div class='modal-dialog'>
|
||||
<div class='modal-content'>
|
||||
<div class="modal-header">
|
||||
@ -79,7 +79,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='modal fade modal-fixed-footer' tabindex='-1' role='dialog' id='modal-alert-dialog'>
|
||||
<div class='modal fade modal-fixed-footer' role='dialog' id='modal-alert-dialog'>
|
||||
<div class='modal-dialog'>
|
||||
<div class='modal-content'>
|
||||
<div class="modal-header">
|
||||
|
Reference in New Issue
Block a user