mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 12:06:44 +00:00
Sales order status update (#4542)
* Add extra status code "IN PROGRESS" for sales order * Add method to 'issue' a SalesOrder * Updates to salesorder template * Add API endpoint and serializer for issuing a SalesOrder * javascript for issuing order * Cleanup buttons for SalesOrderLineItem table * Bug fixes
This commit is contained in:
parent
d0bf4cb81a
commit
f4f7803e96
@ -131,6 +131,7 @@ class SalesOrderStatus(StatusCode):
|
|||||||
"""Defines a set of status codes for a SalesOrder."""
|
"""Defines a set of status codes for a SalesOrder."""
|
||||||
|
|
||||||
PENDING = 10 # Order is pending
|
PENDING = 10 # Order is pending
|
||||||
|
IN_PROGRESS = 15 # Order has been issued, and is in progress
|
||||||
SHIPPED = 20 # Order has been shipped to customer
|
SHIPPED = 20 # Order has been shipped to customer
|
||||||
CANCELLED = 40 # Order has been cancelled
|
CANCELLED = 40 # Order has been cancelled
|
||||||
LOST = 50 # Order was lost
|
LOST = 50 # Order was lost
|
||||||
@ -138,6 +139,7 @@ class SalesOrderStatus(StatusCode):
|
|||||||
|
|
||||||
options = {
|
options = {
|
||||||
PENDING: _("Pending"),
|
PENDING: _("Pending"),
|
||||||
|
IN_PROGRESS: _("In Progress"),
|
||||||
SHIPPED: _("Shipped"),
|
SHIPPED: _("Shipped"),
|
||||||
CANCELLED: _("Cancelled"),
|
CANCELLED: _("Cancelled"),
|
||||||
LOST: _("Lost"),
|
LOST: _("Lost"),
|
||||||
@ -146,6 +148,7 @@ class SalesOrderStatus(StatusCode):
|
|||||||
|
|
||||||
colors = {
|
colors = {
|
||||||
PENDING: 'secondary',
|
PENDING: 'secondary',
|
||||||
|
IN_PROGRESS: 'primary',
|
||||||
SHIPPED: 'success',
|
SHIPPED: 'success',
|
||||||
CANCELLED: 'danger',
|
CANCELLED: 'danger',
|
||||||
LOST: 'warning',
|
LOST: 'warning',
|
||||||
@ -155,6 +158,7 @@ class SalesOrderStatus(StatusCode):
|
|||||||
# Open orders
|
# Open orders
|
||||||
OPEN = [
|
OPEN = [
|
||||||
PENDING,
|
PENDING,
|
||||||
|
IN_PROGRESS,
|
||||||
]
|
]
|
||||||
|
|
||||||
# Completed orders
|
# Completed orders
|
||||||
|
@ -918,6 +918,8 @@ class SalesOrderExtraLineItemMetadata(RetrieveUpdateAPI):
|
|||||||
class SalesOrderContextMixin:
|
class SalesOrderContextMixin:
|
||||||
"""Mixin to add sales order object as serializer context variable."""
|
"""Mixin to add sales order object as serializer context variable."""
|
||||||
|
|
||||||
|
queryset = models.SalesOrder.objects.all()
|
||||||
|
|
||||||
def get_serializer_context(self):
|
def get_serializer_context(self):
|
||||||
"""Add the 'order' reference to the serializer context for any classes which inherit this mixin"""
|
"""Add the 'order' reference to the serializer context for any classes which inherit this mixin"""
|
||||||
ctx = super().get_serializer_context()
|
ctx = super().get_serializer_context()
|
||||||
@ -934,15 +936,17 @@ class SalesOrderContextMixin:
|
|||||||
|
|
||||||
class SalesOrderCancel(SalesOrderContextMixin, CreateAPI):
|
class SalesOrderCancel(SalesOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to cancel a SalesOrder"""
|
"""API endpoint to cancel a SalesOrder"""
|
||||||
|
|
||||||
queryset = models.SalesOrder.objects.all()
|
|
||||||
serializer_class = serializers.SalesOrderCancelSerializer
|
serializer_class = serializers.SalesOrderCancelSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class SalesOrderIssue(SalesOrderContextMixin, CreateAPI):
|
||||||
|
"""API endpoint to issue a SalesOrder"""
|
||||||
|
serializer_class = serializers.SalesOrderIssueSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderComplete(SalesOrderContextMixin, CreateAPI):
|
class SalesOrderComplete(SalesOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for manually marking a SalesOrder as "complete"."""
|
"""API endpoint for manually marking a SalesOrder as "complete"."""
|
||||||
|
|
||||||
queryset = models.SalesOrder.objects.all()
|
|
||||||
serializer_class = serializers.SalesOrderCompleteSerializer
|
serializer_class = serializers.SalesOrderCompleteSerializer
|
||||||
|
|
||||||
|
|
||||||
@ -1649,6 +1653,7 @@ order_api_urls = [
|
|||||||
re_path(r'^allocate/', SalesOrderAllocate.as_view(), name='api-so-allocate'),
|
re_path(r'^allocate/', SalesOrderAllocate.as_view(), name='api-so-allocate'),
|
||||||
re_path(r'^allocate-serials/', SalesOrderAllocateSerials.as_view(), name='api-so-allocate-serials'),
|
re_path(r'^allocate-serials/', SalesOrderAllocateSerials.as_view(), name='api-so-allocate-serials'),
|
||||||
re_path(r'^cancel/', SalesOrderCancel.as_view(), name='api-so-cancel'),
|
re_path(r'^cancel/', SalesOrderCancel.as_view(), name='api-so-cancel'),
|
||||||
|
re_path(r'^issue/', SalesOrderIssue.as_view(), name='api-so-issue'),
|
||||||
re_path(r'^complete/', SalesOrderComplete.as_view(), name='api-so-complete'),
|
re_path(r'^complete/', SalesOrderComplete.as_view(), name='api-so-complete'),
|
||||||
re_path(r'^metadata/', SalesOrderMetadata.as_view(), name='api-so-metadata'),
|
re_path(r'^metadata/', SalesOrderMetadata.as_view(), name='api-so-metadata'),
|
||||||
|
|
||||||
|
18
InvenTree/order/migrations/0087_alter_salesorder_status.py
Normal file
18
InvenTree/order/migrations/0087_alter_salesorder_status.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 3.2.18 on 2023-03-30 11:50
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('order', '0086_auto_20230323_1108'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salesorder',
|
||||||
|
name='status',
|
||||||
|
field=models.PositiveIntegerField(choices=[(10, 'Pending'), (15, 'In Progress'), (20, 'Shipped'), (40, 'Cancelled'), (50, 'Lost'), (60, 'Returned')], default=10, help_text='Purchase order status', verbose_name='Status'),
|
||||||
|
),
|
||||||
|
]
|
@ -770,6 +770,11 @@ class SalesOrder(TotalPriceMixin, Order):
|
|||||||
"""Return True if this order is 'pending'"""
|
"""Return True if this order is 'pending'"""
|
||||||
return self.status == SalesOrderStatus.PENDING
|
return self.status == SalesOrderStatus.PENDING
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_open(self):
|
||||||
|
"""Return True if this order is 'open' (either 'pending' or 'in_progress')"""
|
||||||
|
return self.status in SalesOrderStatus.OPEN
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def stock_allocations(self):
|
def stock_allocations(self):
|
||||||
"""Return a queryset containing all allocations for this order."""
|
"""Return a queryset containing all allocations for this order."""
|
||||||
@ -827,6 +832,21 @@ class SalesOrder(TotalPriceMixin, Order):
|
|||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def place_order(self):
|
||||||
|
"""Deprecated version of 'issue_order'"""
|
||||||
|
self.issue_order()
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def issue_order(self):
|
||||||
|
"""Change this order from 'PENDING' to 'IN_PROGRESS'"""
|
||||||
|
|
||||||
|
if self.status == SalesOrderStatus.PENDING:
|
||||||
|
self.status = SalesOrderStatus.IN_PROGRESS
|
||||||
|
self.issue_date = datetime.now().date()
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
trigger_event('salesorder.issued', id=self.pk)
|
||||||
|
|
||||||
def complete_order(self, user, **kwargs):
|
def complete_order(self, user, **kwargs):
|
||||||
"""Mark this order as "complete."""
|
"""Mark this order as "complete."""
|
||||||
if not self.can_complete(**kwargs):
|
if not self.can_complete(**kwargs):
|
||||||
@ -1717,8 +1737,12 @@ class ReturnOrder(TotalPriceMixin, Order):
|
|||||||
|
|
||||||
trigger_event('returnorder.completed', id=self.pk)
|
trigger_event('returnorder.completed', id=self.pk)
|
||||||
|
|
||||||
@transaction.atomic
|
|
||||||
def place_order(self):
|
def place_order(self):
|
||||||
|
"""Deprecated version of 'issue_order"""
|
||||||
|
self.issue_order()
|
||||||
|
|
||||||
|
@transaction.atomic
|
||||||
|
def issue_order(self):
|
||||||
"""Issue this ReturnOrder (if currently pending)"""
|
"""Issue this ReturnOrder (if currently pending)"""
|
||||||
|
|
||||||
if self.status == ReturnOrderStatus.PENDING:
|
if self.status == ReturnOrderStatus.PENDING:
|
||||||
@ -1726,7 +1750,7 @@ class ReturnOrder(TotalPriceMixin, Order):
|
|||||||
self.issue_date = datetime.now().date()
|
self.issue_date = datetime.now().date()
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
trigger_event('returnorder.placed', id=self.pk)
|
trigger_event('returnorder.issued', id=self.pk)
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def receive_line_item(self, line, location, user, note=''):
|
def receive_line_item(self, line, location, user, note=''):
|
||||||
|
@ -731,6 +731,19 @@ class SalesOrderSerializer(TotalPriceMixin, AbstractOrderSerializer, InvenTreeMo
|
|||||||
customer_detail = CompanyBriefSerializer(source='customer', many=False, read_only=True)
|
customer_detail = CompanyBriefSerializer(source='customer', many=False, read_only=True)
|
||||||
|
|
||||||
|
|
||||||
|
class SalesOrderIssueSerializer(serializers.Serializer):
|
||||||
|
"""Serializer for issuing a SalesOrder"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options"""
|
||||||
|
fields = []
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
"""Save the serializer to 'issue' the order"""
|
||||||
|
order = self.context['order']
|
||||||
|
order.issue_order()
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
|
class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for the SalesOrderAllocation model.
|
"""Serializer for the SalesOrderAllocation model.
|
||||||
|
|
||||||
@ -1461,7 +1474,7 @@ class ReturnOrderIssueSerializer(serializers.Serializer):
|
|||||||
def save(self):
|
def save(self):
|
||||||
"""Save the serializer to 'issue' the order"""
|
"""Save the serializer to 'issue' the order"""
|
||||||
order = self.context['order']
|
order = self.context['order']
|
||||||
order.place_order()
|
order.issue_order()
|
||||||
|
|
||||||
|
|
||||||
class ReturnOrderCancelSerializer(serializers.Serializer):
|
class ReturnOrderCancelSerializer(serializers.Serializer):
|
||||||
|
@ -62,8 +62,8 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
{% if order.status == ReturnOrderStatus.PENDING %}
|
{% if order.status == ReturnOrderStatus.PENDING %}
|
||||||
<button type='button' class='btn btn-primary' id='submit-order' title='{% trans "Submit Order" %}'>
|
<button type='button' class='btn btn-primary' id='issue-order' title='{% trans "Issue Order" %}'>
|
||||||
<span class='fas fa-paper-plane'></span> {% trans "Submit Order" %}
|
<span class='fas fa-paper-plane'></span> {% trans "Issue Order" %}
|
||||||
</button>
|
</button>
|
||||||
{% elif order.status == ReturnOrderStatus.IN_PROGRESS %}
|
{% elif order.status == ReturnOrderStatus.IN_PROGRESS %}
|
||||||
<button type='button' class='btn btn-success' id='complete-order' title='{% trans "Mark order as complete" %}'>
|
<button type='button' class='btn btn-success' id='complete-order' title='{% trans "Mark order as complete" %}'>
|
||||||
@ -186,7 +186,7 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
{% if roles.return_order.change %}
|
{% if roles.return_order.change %}
|
||||||
|
|
||||||
{% if order.status == ReturnOrderStatus.PENDING %}
|
{% if order.status == ReturnOrderStatus.PENDING %}
|
||||||
$('#submit-order').click(function() {
|
$('#issue-order').click(function() {
|
||||||
issueReturnOrder({{ order.pk }}, {
|
issueReturnOrder({{ order.pk }}, {
|
||||||
reload: true,
|
reload: true,
|
||||||
});
|
});
|
||||||
|
@ -56,18 +56,27 @@ src="{% static 'img/blank_image.png' %}"
|
|||||||
</button>
|
</button>
|
||||||
<ul class='dropdown-menu' role='menu'>
|
<ul class='dropdown-menu' role='menu'>
|
||||||
<li><a class='dropdown-item' href='#' id='edit-order'><span class='fas fa-edit icon-green'></span> {% trans "Edit order" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='edit-order'><span class='fas fa-edit icon-green'></span> {% trans "Edit order" %}</a></li>
|
||||||
{% if order.status == SalesOrderStatus.PENDING %}
|
{% if order.is_open %}
|
||||||
<li><a class='dropdown-item' href='#' id='complete-order-shipments'><span class='fas fa-truck'></span> {% trans "Complete Shipments" %}</a></li>
|
|
||||||
<li><a class='dropdown-item' href='#' id='cancel-order'><span class='fas fa-times-circle icon-red'></span> {% trans "Cancel order" %}</a></li>
|
<li><a class='dropdown-item' href='#' id='cancel-order'><span class='fas fa-times-circle icon-red'></span> {% trans "Cancel order" %}</a></li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
{% if order.status == SalesOrderStatus.PENDING %}
|
<div class='btn-group' role='group'>
|
||||||
<button type='button' class='btn btn-success' id='complete-order' title='{% trans "Complete Sales Order" %}'>
|
{% if order.is_pending %}
|
||||||
<span class='fas fa-check-circle'></span> {% trans "Complete Order" %}
|
<button type='button' class='btn btn-primary' id='issue-order' title='{% trans "Issue Order" %}'>
|
||||||
</button>
|
<span class='fas fa-paper-plane'></span> {% trans "Issue Order" %}
|
||||||
{% endif %}
|
</button>
|
||||||
|
{% elif order.status == SalesOrderStatus.IN_PROGRESS %}
|
||||||
|
{% if not order.is_completed %}
|
||||||
|
<button type='button' class='btn btn-success' id='complete-order-shipments' title='{% trans "Ship Items" %}'>
|
||||||
|
<span class='fas fa-truck'></span> {% trans "Ship Items" %}
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
<button type='button' class='btn btn-success' id='complete-order' title='{% trans "Complete Sales Order" %}'>
|
||||||
|
<span class='fas fa-check-circle'></span> {% trans "Complete Order" %}
|
||||||
|
</button>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock actions %}
|
{% endblock actions %}
|
||||||
|
|
||||||
@ -232,6 +241,15 @@ $("#complete-order-shipments").click(function() {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
$('#issue-order').click(function() {
|
||||||
|
issueSalesOrder(
|
||||||
|
{{ order.pk }},
|
||||||
|
{
|
||||||
|
reload: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
$("#cancel-order").click(function() {
|
$("#cancel-order").click(function() {
|
||||||
|
|
||||||
cancelSalesOrder(
|
cancelSalesOrder(
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
{% include "spacer.html" %}
|
{% include "spacer.html" %}
|
||||||
<div class='btn-group' role='group'>
|
<div class='btn-group' role='group'>
|
||||||
{% if roles.sales_order.add %}
|
{% if roles.sales_order.add %}
|
||||||
{% if order.is_pending or allow_extra_editing %}
|
{% if order.is_open or allow_extra_editing %}
|
||||||
<button type='button' class='btn btn-success' id='new-so-line'>
|
<button type='button' class='btn btn-success' id='new-so-line'>
|
||||||
<span class='fas fa-plus-circle'></span> {% trans "Add Line Item" %}
|
<span class='fas fa-plus-circle'></span> {% trans "Add Line Item" %}
|
||||||
</button>
|
</button>
|
||||||
@ -44,7 +44,7 @@
|
|||||||
{% include "spacer.html" %}
|
{% include "spacer.html" %}
|
||||||
<div class='btn-group' role='group'>
|
<div class='btn-group' role='group'>
|
||||||
{% if roles.sales_order.change %}
|
{% if roles.sales_order.change %}
|
||||||
{% if order.is_pending or allow_extra_editing %}
|
{% if order.is_open or allow_extra_editing %}
|
||||||
<button type='button' class='btn btn-success' id='new-so-extra-line'>
|
<button type='button' class='btn btn-success' id='new-so-extra-line'>
|
||||||
<span class='fas fa-plus-circle'></span> {% trans "Add Extra Line" %}
|
<span class='fas fa-plus-circle'></span> {% trans "Add Extra Line" %}
|
||||||
</button>
|
</button>
|
||||||
@ -253,11 +253,18 @@
|
|||||||
order: {{ order.pk }},
|
order: {{ order.pk }},
|
||||||
reference: '{{ order.reference }}',
|
reference: '{{ order.reference }}',
|
||||||
status: {{ order.status }},
|
status: {{ order.status }},
|
||||||
|
open: {% js_bool order.is_open %},
|
||||||
{% if roles.sales_order.change %}
|
{% if roles.sales_order.change %}
|
||||||
|
{% settings_value "SALESORDER_EDIT_COMPLETED_ORDERS" as allow_edit %}
|
||||||
|
{% if allow_edit or order.is_open %}
|
||||||
allow_edit: true,
|
allow_edit: true,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if order.is_pending %}
|
{% if order.status == SalesOrderStatus.IN_PROGRESS %}
|
||||||
pending: true,
|
allow_ship: true,
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% if roles.sales_order.delete %}
|
||||||
|
allow_delete: true,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -281,7 +288,7 @@
|
|||||||
name: 'salesorderextraline',
|
name: 'salesorderextraline',
|
||||||
filtertarget: '#filter-list-sales-order-extra-lines',
|
filtertarget: '#filter-list-sales-order-extra-lines',
|
||||||
{% settings_value "SALESORDER_EDIT_COMPLETED_ORDERS" as allow_edit %}
|
{% settings_value "SALESORDER_EDIT_COMPLETED_ORDERS" as allow_edit %}
|
||||||
{% if order.is_pending or allow_edit %}
|
{% if order.is_open or allow_edit %}
|
||||||
allow_edit: {% js_bool roles.sales_order.change %},
|
allow_edit: {% js_bool roles.sales_order.change %},
|
||||||
allow_delete: {% js_bool roles.sales_order.delete %},
|
allow_delete: {% js_bool roles.sales_order.delete %},
|
||||||
{% else %}
|
{% else %}
|
||||||
|
@ -362,10 +362,16 @@ function renderSalesOrder(data, parameters={}) {
|
|||||||
image = data.customer_detail.thumbnail || data.customer_detail.image || blankImage();
|
image = data.customer_detail.thumbnail || data.customer_detail.image || blankImage();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let text = data.reference;
|
||||||
|
|
||||||
|
if (data.customer_detail) {
|
||||||
|
text += ` - ${data.customer_detail.name}`;
|
||||||
|
}
|
||||||
|
|
||||||
return renderModel(
|
return renderModel(
|
||||||
{
|
{
|
||||||
image: image,
|
image: image,
|
||||||
text: `${data.reference} - ${data.customer_detail.name}`,
|
text: text,
|
||||||
textSecondary: shortenString(data.description),
|
textSecondary: shortenString(data.description),
|
||||||
url: data.url || `/order/sales-order/${data.pk}/`,
|
url: data.url || `/order/sales-order/${data.pk}/`,
|
||||||
},
|
},
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
createSalesOrderShipment,
|
createSalesOrderShipment,
|
||||||
editSalesOrder,
|
editSalesOrder,
|
||||||
exportOrder,
|
exportOrder,
|
||||||
|
issueSalesOrder,
|
||||||
loadSalesOrderAllocationTable,
|
loadSalesOrderAllocationTable,
|
||||||
loadSalesOrderLineItemTable,
|
loadSalesOrderLineItemTable,
|
||||||
loadSalesOrderShipmentTable,
|
loadSalesOrderShipmentTable,
|
||||||
@ -456,6 +457,28 @@ function completeSalesOrder(order_id, options={}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Launches sa modal form to mark a SalesOrder as "issued"
|
||||||
|
*/
|
||||||
|
function issueSalesOrder(order_id, options={}) {
|
||||||
|
|
||||||
|
let html = `
|
||||||
|
<div class='alert alert-block alert-info'>
|
||||||
|
{% trans "Issue this Sales Order?" %}
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
constructForm(`{% url "api-so-list" %}${order_id}/issue/`, {
|
||||||
|
method: 'POST',
|
||||||
|
title: '{% trans "Issue Sales Order" %}',
|
||||||
|
confirm: true,
|
||||||
|
preFormContent: html,
|
||||||
|
onSuccess: function(response) {
|
||||||
|
handleFormSuccess(response, options);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Launches a modal form to mark a SalesOrder as "cancelled"
|
* Launches a modal form to mark a SalesOrder as "cancelled"
|
||||||
*/
|
*/
|
||||||
@ -1597,10 +1620,6 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
|
|
||||||
options.table = table;
|
options.table = table;
|
||||||
|
|
||||||
if (!options.pending && !global_settings.SALESORDER_EDIT_COMPLETED_ORDERS) {
|
|
||||||
options.allow_edit = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
options.params = options.params || {};
|
options.params = options.params || {};
|
||||||
|
|
||||||
if (!options.order) {
|
if (!options.order) {
|
||||||
@ -1632,14 +1651,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Is the order pending?
|
var show_detail = true;
|
||||||
var pending = options.pending;
|
|
||||||
|
|
||||||
// Has the order shipped?
|
|
||||||
var shipped = options.status == {{ SalesOrderStatus.SHIPPED }};
|
|
||||||
|
|
||||||
// Show detail view if the PurchaseOrder is PENDING or SHIPPED
|
|
||||||
var show_detail = pending || shipped;
|
|
||||||
|
|
||||||
// Add callbacks for expand / collapse buttons
|
// Add callbacks for expand / collapse buttons
|
||||||
$('#sales-lines-expand').click(function() {
|
$('#sales-lines-expand').click(function() {
|
||||||
@ -1750,7 +1762,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
if (pending) {
|
if (options.open) {
|
||||||
columns.push(
|
columns.push(
|
||||||
{
|
{
|
||||||
field: 'stock',
|
field: 'stock',
|
||||||
@ -1843,25 +1855,22 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
title: '{% trans "Notes" %}',
|
title: '{% trans "Notes" %}',
|
||||||
});
|
});
|
||||||
|
|
||||||
if (pending) {
|
columns.push({
|
||||||
columns.push({
|
field: 'buttons',
|
||||||
field: 'buttons',
|
switchable: false,
|
||||||
switchable: false,
|
formatter: function(value, row, index, field) {
|
||||||
formatter: function(value, row, index, field) {
|
let pk = row.pk;
|
||||||
|
let buttons = '';
|
||||||
|
|
||||||
let buttons = '';
|
// Construct a set of buttons to display
|
||||||
|
if (row.part && row.part_detail) {
|
||||||
var pk = row.pk;
|
let part = row.part_detail;
|
||||||
|
|
||||||
if (row.part) {
|
|
||||||
var part = row.part_detail;
|
|
||||||
|
|
||||||
|
if (options.allow_edit && !row.shipped) {
|
||||||
if (part.trackable) {
|
if (part.trackable) {
|
||||||
buttons += makeIconButton('fa-hashtag icon-green', 'button-add-by-sn', pk, '{% trans "Allocate serial numbers" %}');
|
buttons += makeIconButton('fa-hashtag icon-green', 'button-add-by-sn', pk, '{% trans "Allocate serial numbers" %}');
|
||||||
}
|
}
|
||||||
|
|
||||||
buttons += makeIconButton('fa-sign-in-alt icon-green', 'button-add', pk, '{% trans "Allocate stock" %}');
|
buttons += makeIconButton('fa-sign-in-alt icon-green', 'button-add', pk, '{% trans "Allocate stock" %}');
|
||||||
|
|
||||||
if (part.purchaseable) {
|
if (part.purchaseable) {
|
||||||
buttons += makeIconButton('fa-shopping-cart', 'button-buy', row.part, '{% trans "Purchase stock" %}');
|
buttons += makeIconButton('fa-shopping-cart', 'button-buy', row.part, '{% trans "Purchase stock" %}');
|
||||||
}
|
}
|
||||||
@ -1869,13 +1878,17 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
if (part.assembly) {
|
if (part.assembly) {
|
||||||
buttons += makeIconButton('fa-tools', 'button-build', row.part, '{% trans "Build stock" %}');
|
buttons += makeIconButton('fa-tools', 'button-build', row.part, '{% trans "Build stock" %}');
|
||||||
}
|
}
|
||||||
|
|
||||||
buttons += makeIconButton('fa-dollar-sign icon-green', 'button-price', pk, '{% trans "Calculate price" %}');
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buttons += makeIconButton('fa-dollar-sign icon-green', 'button-price', pk, '{% trans "Calculate price" %}');
|
||||||
|
|
||||||
|
if (options.allow_edit) {
|
||||||
buttons += makeCopyButton('button-duplicate', pk, '{% trans "Duplicate line item" %}');
|
buttons += makeCopyButton('button-duplicate', pk, '{% trans "Duplicate line item" %}');
|
||||||
buttons += makeEditButton('button-edit', pk, '{% trans "Edit line item" %}');
|
buttons += makeEditButton('button-edit', pk, '{% trans "Edit line item" %}');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.allow_delete) {
|
||||||
var delete_disabled = false;
|
var delete_disabled = false;
|
||||||
|
|
||||||
var title = '{% trans "Delete line item" %}';
|
var title = '{% trans "Delete line item" %}';
|
||||||
@ -1890,11 +1903,11 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
|
|
||||||
// Prevent deletion of the line item if items have been allocated or shipped!
|
// Prevent deletion of the line item if items have been allocated or shipped!
|
||||||
buttons += makeDeleteButton('button-delete', pk, title, {disabled: delete_disabled});
|
buttons += makeDeleteButton('button-delete', pk, title, {disabled: delete_disabled});
|
||||||
|
|
||||||
return wrapButtons(buttons);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
return wrapButtons(buttons);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
function reloadTable() {
|
function reloadTable() {
|
||||||
$(table).bootstrapTable('refresh');
|
$(table).bootstrapTable('refresh');
|
||||||
@ -1954,7 +1967,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
{
|
{
|
||||||
success: function(response) {
|
success: function(response) {
|
||||||
|
|
||||||
constructForm(`{% url "api-so-line-list" %}${options.order}/allocate-serials/`, {
|
constructForm(`{% url "api-so-list" %}${options.order}/allocate-serials/`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
title: '{% trans "Allocate Serial Numbers" %}',
|
title: '{% trans "Allocate Serial Numbers" %}',
|
||||||
fields: {
|
fields: {
|
||||||
@ -2088,7 +2101,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
detailViewByClick: false,
|
detailViewByClick: false,
|
||||||
buttons: constructExpandCollapseButtons(table),
|
buttons: constructExpandCollapseButtons(table),
|
||||||
detailFilter: function(index, row) {
|
detailFilter: function(index, row) {
|
||||||
if (pending) {
|
if (options.open) {
|
||||||
// Order is pending
|
// Order is pending
|
||||||
return row.allocated > 0;
|
return row.allocated > 0;
|
||||||
} else {
|
} else {
|
||||||
@ -2096,7 +2109,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
detailFormatter: function(index, row, element) {
|
detailFormatter: function(index, row, element) {
|
||||||
if (pending) {
|
if (options.open) {
|
||||||
return showAllocationSubTable(index, row, element, options);
|
return showAllocationSubTable(index, row, element, options);
|
||||||
} else {
|
} else {
|
||||||
return showFulfilledSubTable(index, row, element, options);
|
return showFulfilledSubTable(index, row, element, options);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user