Refactorin'

This commit is contained in:
Oliver 2021-07-11 20:36:52 +10:00
parent 14ab1bef14
commit c045a3b6f6
3 changed files with 205 additions and 160 deletions

View File

@ -143,7 +143,7 @@ class StockAdjust(APIView):
elif 'items' in request.data:
_items = request.data['items']
else:
raise ValidationError({'items': 'Request must contain list of stock items'})
raise ValidationError({'items': _('Request must contain list of stock items')})
# List of validated items
self.items = []
@ -151,13 +151,14 @@ class StockAdjust(APIView):
for entry in _items:
if not type(entry) == dict:
raise ValidationError({'error': 'Improperly formatted data'})
raise ValidationError({'error': _('Improperly formatted data')})
try:
pk = entry.get('pk', None)
# Look for 'pk' value first, with 'id' as a backup
pk = entry.get('pk', entry.get('id', None))
item = StockItem.objects.get(pk=pk)
except (ValueError, StockItem.DoesNotExist):
raise ValidationError({'pk': 'Each entry must contain a valid pk field'})
raise ValidationError({'pk': _('Each entry must contain a valid pk field')})
if self.allow_missing_quantity and 'quantity' not in entry:
entry['quantity'] = item.quantity
@ -165,10 +166,10 @@ class StockAdjust(APIView):
try:
quantity = Decimal(str(entry.get('quantity', None)))
except (ValueError, TypeError, InvalidOperation):
raise ValidationError({'quantity': "Each entry must contain a valid quantity value"})
raise ValidationError({'quantity': _("Each entry must contain a valid quantity value")})
if quantity < 0:
raise ValidationError({'quantity': 'Quantity field must not be less than zero'})
raise ValidationError({'quantity': _('Quantity field must not be less than zero')})
self.items.append({
'item': item,

View File

@ -441,7 +441,17 @@ function constructFormBody(fields, options) {
modalEnable(modal, true);
// Insert generated form content
$(modal).find('.modal-form-content').html(html);
$(modal).find('#form-content').html(html);
if (options.preFormContent) {
console.log('pre form content', options.preFormContent);
$(modal).find('#pre-form-content').html(options.preFormContent);
}
if (options.postFormContent) {
console.log('post form content', options.postFormContent);
$(modal).find('#post-form-content').html(options.postFormContent);
}
// Clear any existing buttons from the modal
$(modal).find('#modal-footer-buttons').html('');

View File

@ -20,6 +20,138 @@ function stockStatusCodes() {
}
/**
* Perform stock adjustments
*/
function adjustStock(items, options={}) {
var formTitle = 'Form Title Here';
var actionTitle = null;
switch (options.action) {
case 'move':
formTitle = '{% trans "Transfer Stock" %}';
actionTitle = '{% trans "Move" %}';
break;
case 'count':
formTitle = '{% trans "Count Stock" %}';
actionTitle = '{% trans "Count" %}';
break;
case 'take':
formTitle = '{% trans "Remove Stock" %}';
actionTitle = '{% trans "Take" %}';
break;
case 'add':
formTitle = '{% trans "Add Stock" %}';
actionTitle = '{% trans "Add" %}';
break;
case 'delete':
formTitle = '{% trans "Delete Stock" %}';
break;
default:
break;
}
// Generate modal HTML content
var html = `
<table class='table table-striped table-condensed' id='stock-adjust-table'>
<thead>
<tr>
<th>{% trans "Part" %}</th>
<th>{% trans "Stock" %}</th>
<th>{% trans "Location" %}</th>
<th>${actionTitle || ''}</th>
</tr>
</thead>
<tbody>
`;
items.forEach(function(item) {
var pk = item.pk;
var image = item.part_detail.thumbnail || item.part_detail.image || blankImage();
var status = stockStatusDisplay(item.status, {
classes: 'float-right'
});
var quantity = item.quantity;
var location = locationDetail(item, false);
if (item.location_detail) {
location = item.location_detail.pathstring;
}
if (item.serial != null) {
quantity = `#${item.serial}`;
}
var actionInput = '';
if (actionTitle != null) {
actionInput = constructNumberInput(
item.pk,
{
value: item.quantity,
min_value: 0,
}
)
};
var buttons = `<div class='btn-group float-right' role='group'>`;
buttons += makeIconButton(
'fa-trash-alt icon-red',
'button-stock-item-remove',
pk,
'{% trans "Remove stock item" %}',
);
buttons += `</div>`;
html += `
<tr id='stock_item_${pk}'>
<td id='part_${pk}'><img src='${image}' class='hover-img-thumb'> ${item.part_detail.full_name}</td>
<td id='stock_${pk}'>${quantity}${status}</td>
<td id='location_${pk}'>${location}</td>
<td id='action_${pk}'>
<div>
${actionInput}
${buttons}
</div>
</td>
</tr>`;
});
html += `</tbody></table>`;
var modal = createNewModal({
title: formTitle,
});
constructFormBody({}, {
preFormContent: html,
confirm: true,
modal: modal,
});
// Attach callbacks for the action buttons
$(modal).find('.button-stock-item-remove').click(function() {
var pk = $(this).attr('pk');
$(modal).find(`#stock_item_${pk}`).remove();
});
attachToggle(modal);
$(modal + ' .select2-container').addClass('select-full-width');
$(modal + ' .select2-container').css('width', '100%');
}
function removeStockRow(e) {
// Remove a selected row from a stock modal form
@ -228,6 +360,58 @@ function loadStockTestResultsTable(table, options) {
}
function locationDetail(row, showLink=true) {
/*
* Function to display a "location" of a StockItem.
*
* Complicating factors: A StockItem may not actually *be* in a location!
* - Could be at a customer
* - Could be installed in another stock item
* - Could be assigned to a sales order
* - Could be currently in production!
*
* So, instead of being naive, we'll check!
*/
// Display text
var text = '';
// URL (optional)
var url = '';
if (row.is_building && row.build) {
// StockItem is currently being built!
text = '{% trans "In production" %}';
url = `/build/${row.build}/`;
} else if (row.belongs_to) {
// StockItem is installed inside a different StockItem
text = `{% trans "Installed in Stock Item" %} ${row.belongs_to}`;
url = `/stock/item/${row.belongs_to}/installed/`;
} else if (row.customer) {
// StockItem has been assigned to a customer
text = '{% trans "Shipped to customer" %}';
url = `/company/${row.customer}/assigned-stock/`;
} else if (row.sales_order) {
// StockItem has been assigned to a sales order
text = '{% trans "Assigned to Sales Order" %}';
url = `/order/sales-order/${row.sales_order}/`;
} else if (row.location) {
text = row.location_detail.pathstring;
url = `/stock/location/${row.location}/`;
} else {
text = '<i>{% trans "No stock location set" %}</i>';
url = '';
}
if (showLink && url) {
return renderLink(text, url);
} else {
return text;
}
}
function loadStockTable(table, options) {
/* Load data into a stock table with adjustable options.
* Fetches data (via AJAX) and loads into a bootstrap table.
@ -271,56 +455,6 @@ function loadStockTable(table, options) {
filters[key] = params[key];
}
function locationDetail(row) {
/*
* Function to display a "location" of a StockItem.
*
* Complicating factors: A StockItem may not actually *be* in a location!
* - Could be at a customer
* - Could be installed in another stock item
* - Could be assigned to a sales order
* - Could be currently in production!
*
* So, instead of being naive, we'll check!
*/
// Display text
var text = '';
// URL (optional)
var url = '';
if (row.is_building && row.build) {
// StockItem is currently being built!
text = '{% trans "In production" %}';
url = `/build/${row.build}/`;
} else if (row.belongs_to) {
// StockItem is installed inside a different StockItem
text = `{% trans "Installed in Stock Item" %} ${row.belongs_to}`;
url = `/stock/item/${row.belongs_to}/installed/`;
} else if (row.customer) {
// StockItem has been assigned to a customer
text = '{% trans "Shipped to customer" %}';
url = `/company/${row.customer}/assigned-stock/`;
} else if (row.sales_order) {
// StockItem has been assigned to a sales order
text = '{% trans "Assigned to Sales Order" %}';
url = `/order/sales-order/${row.sales_order}/`;
} else if (row.location) {
text = row.location_detail.pathstring;
url = `/stock/location/${row.location}/`;
} else {
text = '<i>{% trans "No stock location set" %}</i>';
url = '';
}
if (url) {
return renderLink(text, url);
} else {
return text;
}
}
var grouping = true;
if ('grouping' in options) {
@ -741,114 +875,14 @@ function loadStockTable(table, options) {
]
);
function stockAdjustment(action) {
var items = $("#stock-table").bootstrapTable("getSelections");
var stock = [];
items.forEach(function(item) {
stock.push(item.pk);
adjustStock(items, {
action: action,
});
var title = 'Form title';
switch (action) {
case 'move':
title = '{% trans "Transfer Stock" %}';
break;
case 'count':
title = '{% trans "Count Stock" %}';
break;
case 'take':
title = '{% trans "Remove Stock" %}';
break;
case 'add':
title = '{% trans "Add Stock" %}';
break;
case 'delete':
title = '{% trans "Delete Stock" %}';
break;
default:
break;
}
var modal = createNewModal({
title: title,
});
// Generate content for the modal
var html = `
<table class='table table-striped table-condensed' id='stock-adjust-table'>
<thead>
<tr>
<th>{% trans "Part" %}</th>
<th>{% trans "Stock" %}</th>
<th>{% trans "Location" %}</th>
<th></th>
</tr>
</thead>
<tbody>
`;
items.forEach(function(item) {
var pk = item.pk;
var image = item.part_detail.thumbnail || item.part_detail.image || blankImage();
var status = stockStatusDisplay(item.status, {
classes: 'float-right'
});
var quantity = item.quantity;
if (item.serial != null) {
quantity = `#${item.serial}`;
}
var buttons = `<div class='btn-group float-right' role='group'>`;
buttons += makeIconButton(
'fa-trash-alt icon-red',
'button-stock-item-remove',
pk,
'{% trans "Remove stock item" %}',
);
buttons += `</div>`;
html += `
<tr id='stock_item_${pk}'>
<td id='part_${pk}'><img src='${image}' class='hover-img-thumb'> ${item.part_detail.full_name}${status}</td>
<td id='stock_${pk}'>${quantity}</td>
<td id='location_${pk}'>${item.location_detail.pathstring}</td>
<td id='action_${pk}'>${buttons}</td>
</tr>`;
});
html += `</tbody></table>`;
$(modal).find('.modal-form-content').html(html);
// Attach callbacks for the action buttons
$(modal).find('.button-stock-item-remove').click(function() {
var pk = $(this).attr('pk');
$(modal).find(`#stock_item_${pk}`).remove();
});
// Add a "confirm" button
insertConfirmButton({
modal: modal,
});
attachToggle(modal);
$(modal + ' .select2-container').addClass('select-full-width');
$(modal + ' .select2-container').css('width', '100%');
return;
// Buttons for launching secondary modals