mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 20:16:44 +00:00
* Squashed commit of the following: commit 52d7ff0f650bbcfa2d93ac96562b44269d3812a7 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 23:03:20 2024 +0100 fixed lookup commit 0d076eaea89dce24f08af247479b3b4dff1b4df3 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 23:03:08 2024 +0100 switched to pathlib for lookup commit 473e75eda205793769946e923748356ffd7e5b4b Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 22:52:30 2024 +0100 fix wrong url response commit fd74f8d703399c19cb3616ea3b2656a50cd7a6e5 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 21:14:38 2024 +0100 switched to ruff for import sorting commit f83fedbbb8de261ff8c706e179519e58e7a91064 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 21:03:14 2024 +0100 switched to single quotes everywhere commit a92442e60e23be0ff5dcf42d222b0d95823ecb9b Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:58:23 2024 +0100 added autofixes commit cc66c93136fcae8a701810a4f4f38ef3b570be61 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:56:47 2024 +0100 enable autoformat commit 1f343606ec1f2a99acf8a37b9900d78a8fb37282 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:42:14 2024 +0100 Squashed commit of the following: commit f5cf7b2e7872fc19633321713965763d1890b495 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:36:57 2024 +0100 fixed reqs commit 9d845bee98befa4e53c2ac3c783bd704369e3ad2 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:32:35 2024 +0100 disable autofix/format commit aff5f271484c3500df7ddde043767c008ce4af21 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:28:50 2024 +0100 adjust checks commit 47271cf1efa848ec8374a0d83b5646d06fffa6e7 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:28:22 2024 +0100 reorder order of operations commit e1bf178b40b3f0d2d59ba92209156c43095959d2 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:01:09 2024 +0100 adapted ruff settings to better fit code base commit ad7d88a6f4f15c9552522131c4e207256fc2bbf6 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:59:45 2024 +0100 auto fixed docstring commit a2e54a760e17932dbbc2de0dec23906107f2cda9 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:46:35 2024 +0100 fix getattr useage commit cb80c73bc6c0be7f5d2ed3cc9b2ac03fdefd5c41 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:25:09 2024 +0100 fix requirements file commit b7780bbd21a32007f3b0ce495b519bf59bb19bf5 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:42:28 2024 +0100 fix removed sections commit 71f1681f55c15f62c16c1d7f30a745adc496db97 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:41:21 2024 +0100 fix djlint syntax commit a0bcf1bccef8a8ffd482f38e2063bc9066e1d759 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:35:28 2024 +0100 remove flake8 from code base commit 22475b31cc06919785be046e007915e43f356793 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:34:56 2024 +0100 remove flake8 from code base commit 0413350f14773ac6161473e0cfb069713c13c691 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:24:39 2024 +0100 moved ruff section commit d90c48a0bf98befdfacbbb093ee56cdb28afb40d Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:24:24 2024 +0100 move djlint config to pyproject commit c5ce55d5119bf2e35e429986f62f875c86178ae1 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:20:39 2024 +0100 added isort again commit 42a41d23afc280d4ee6f0e640148abc6f460f05a Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:19:02 2024 +0100 move config section commit 85692331816348cb1145570340d1f6488a8265cc Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:17:52 2024 +0100 fix codespell error commit 2897c6704d1311a800ce5aa47878d96d6980b377 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 17:29:21 2024 +0100 replaced flake8 with ruff mostly for speed improvements * enable docstring checks * fix docstrings * fixed D417 Missing argument description * Squashed commit of the following: commit d3b795824b5d6d1c0eda67150b45b5cd672b3f6b Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 22:56:17 2024 +0100 fixed source path commit 0bac0c19b88897a19d5c995e4ff50427718b827e Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 22:47:53 2024 +0100 fixed req commit 9f61f01d9cc01f1fb7123102f3658c890469b8ce Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 22:45:18 2024 +0100 added missing toml req commit 91b71ed24a6761b629768d0ad8829fec2819a966 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:49:50 2024 +0100 moved isort config commit 12460b04196b12d0272d40552402476d5492fea5 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:43:22 2024 +0100 remove flake8 section from setup.cfg commit f5cf7b2e7872fc19633321713965763d1890b495 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:36:57 2024 +0100 fixed reqs commit 9d845bee98befa4e53c2ac3c783bd704369e3ad2 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:32:35 2024 +0100 disable autofix/format commit aff5f271484c3500df7ddde043767c008ce4af21 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:28:50 2024 +0100 adjust checks commit 47271cf1efa848ec8374a0d83b5646d06fffa6e7 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:28:22 2024 +0100 reorder order of operations commit e1bf178b40b3f0d2d59ba92209156c43095959d2 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:01:09 2024 +0100 adapted ruff settings to better fit code base commit ad7d88a6f4f15c9552522131c4e207256fc2bbf6 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:59:45 2024 +0100 auto fixed docstring commit a2e54a760e17932dbbc2de0dec23906107f2cda9 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:46:35 2024 +0100 fix getattr useage commit cb80c73bc6c0be7f5d2ed3cc9b2ac03fdefd5c41 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:25:09 2024 +0100 fix requirements file commit b7780bbd21a32007f3b0ce495b519bf59bb19bf5 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:42:28 2024 +0100 fix removed sections commit 71f1681f55c15f62c16c1d7f30a745adc496db97 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:41:21 2024 +0100 fix djlint syntax commit a0bcf1bccef8a8ffd482f38e2063bc9066e1d759 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:35:28 2024 +0100 remove flake8 from code base commit 22475b31cc06919785be046e007915e43f356793 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:34:56 2024 +0100 remove flake8 from code base commit 0413350f14773ac6161473e0cfb069713c13c691 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:24:39 2024 +0100 moved ruff section commit d90c48a0bf98befdfacbbb093ee56cdb28afb40d Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:24:24 2024 +0100 move djlint config to pyproject commit c5ce55d5119bf2e35e429986f62f875c86178ae1 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:20:39 2024 +0100 added isort again commit 42a41d23afc280d4ee6f0e640148abc6f460f05a Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:19:02 2024 +0100 move config section commit 85692331816348cb1145570340d1f6488a8265cc Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:17:52 2024 +0100 fix codespell error commit 2897c6704d1311a800ce5aa47878d96d6980b377 Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 17:29:21 2024 +0100 replaced flake8 with ruff mostly for speed improvements * fix pyproject * make docstrings more uniform * auto-format * fix order * revert url change
441 lines
15 KiB
Python
441 lines
15 KiB
Python
"""Django views for interacting with Order app."""
|
|
|
|
import logging
|
|
from decimal import Decimal, InvalidOperation
|
|
|
|
from django.db.utils import IntegrityError
|
|
from django.forms import HiddenInput, IntegerField
|
|
from django.http import HttpResponseRedirect
|
|
from django.http.response import JsonResponse
|
|
from django.shortcuts import get_object_or_404
|
|
from django.urls import reverse
|
|
from django.utils.translation import gettext_lazy as _
|
|
from django.views.generic import DetailView, ListView
|
|
|
|
from common.files import FileManager
|
|
from common.forms import MatchFieldForm, UploadFileForm
|
|
from common.views import FileManagementFormView
|
|
from company.models import SupplierPart # ManufacturerPart
|
|
from InvenTree.helpers import DownloadFile
|
|
from InvenTree.views import AjaxView, InvenTreeRoleMixin
|
|
from part.models import Part
|
|
from part.views import PartPricing
|
|
from plugin.views import InvenTreePluginViewMixin
|
|
|
|
from . import forms as order_forms
|
|
from .admin import PurchaseOrderLineItemResource, SalesOrderLineItemResource
|
|
from .models import (
|
|
PurchaseOrder,
|
|
PurchaseOrderLineItem,
|
|
ReturnOrder,
|
|
SalesOrder,
|
|
SalesOrderLineItem,
|
|
)
|
|
|
|
logger = logging.getLogger('inventree')
|
|
|
|
|
|
class PurchaseOrderIndex(InvenTreeRoleMixin, ListView):
|
|
"""List view for all purchase orders."""
|
|
|
|
model = PurchaseOrder
|
|
template_name = 'order/purchase_orders.html'
|
|
context_object_name = 'orders'
|
|
|
|
def get_queryset(self):
|
|
"""Retrieve the list of purchase orders, ensure that the most recent ones are returned first."""
|
|
queryset = PurchaseOrder.objects.all().order_by('-creation_date')
|
|
|
|
return queryset
|
|
|
|
|
|
class SalesOrderIndex(InvenTreeRoleMixin, ListView):
|
|
"""SalesOrder index (list) view class."""
|
|
|
|
model = SalesOrder
|
|
template_name = 'order/sales_orders.html'
|
|
context_object_name = 'orders'
|
|
|
|
|
|
class ReturnOrderIndex(InvenTreeRoleMixin, ListView):
|
|
"""ReturnOrder index (list) view."""
|
|
|
|
model = ReturnOrder
|
|
template_name = 'order/return_orders.html'
|
|
context_object_name = 'orders'
|
|
|
|
|
|
class PurchaseOrderDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView):
|
|
"""Detail view for a PurchaseOrder object."""
|
|
|
|
context_object_name = 'order'
|
|
queryset = PurchaseOrder.objects.all().prefetch_related('lines')
|
|
template_name = 'order/purchase_order_detail.html'
|
|
|
|
|
|
class SalesOrderDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView):
|
|
"""Detail view for a SalesOrder object."""
|
|
|
|
context_object_name = 'order'
|
|
queryset = SalesOrder.objects.all().prefetch_related(
|
|
'lines__allocations__item__purchase_order'
|
|
)
|
|
template_name = 'order/sales_order_detail.html'
|
|
|
|
|
|
class ReturnOrderDetail(InvenTreeRoleMixin, InvenTreePluginViewMixin, DetailView):
|
|
"""Detail view for a ReturnOrder object."""
|
|
|
|
context_object_name = 'order'
|
|
queryset = ReturnOrder.objects.all()
|
|
template_name = 'order/return_order_detail.html'
|
|
|
|
|
|
class PurchaseOrderUpload(FileManagementFormView):
|
|
"""PurchaseOrder: Upload file, match to fields and parts (using multi-Step form)."""
|
|
|
|
class OrderFileManager(FileManager):
|
|
"""Specify required fields."""
|
|
|
|
REQUIRED_HEADERS = ['Quantity']
|
|
|
|
ITEM_MATCH_HEADERS = ['Manufacturer_MPN', 'Supplier_SKU']
|
|
|
|
OPTIONAL_HEADERS = ['Purchase_Price', 'Reference', 'Notes']
|
|
|
|
name = 'order'
|
|
form_list = [
|
|
('upload', UploadFileForm),
|
|
('fields', MatchFieldForm),
|
|
('items', order_forms.OrderMatchItemForm),
|
|
]
|
|
form_steps_template = [
|
|
'order/order_wizard/po_upload.html',
|
|
'order/order_wizard/match_fields.html',
|
|
'order/order_wizard/match_parts.html',
|
|
]
|
|
form_steps_description = [
|
|
_('Upload File'),
|
|
_('Match Fields'),
|
|
_('Match Supplier Parts'),
|
|
]
|
|
form_field_map = {
|
|
'item_select': 'part',
|
|
'quantity': 'quantity',
|
|
'purchase_price': 'purchase_price',
|
|
'reference': 'reference',
|
|
'notes': 'notes',
|
|
}
|
|
file_manager_class = OrderFileManager
|
|
|
|
def get_order(self):
|
|
"""Get order or return 404."""
|
|
return get_object_or_404(PurchaseOrder, pk=self.kwargs['pk'])
|
|
|
|
def get_context_data(self, form, **kwargs):
|
|
"""Handle context data for order."""
|
|
context = super().get_context_data(form=form, **kwargs)
|
|
|
|
order = self.get_order()
|
|
|
|
context.update({'order': order})
|
|
|
|
return context
|
|
|
|
def get_field_selection(self):
|
|
"""Once data columns have been selected, attempt to pre-select the proper data from the database.
|
|
|
|
This function is called once the field selection has been validated.
|
|
The pre-fill data are then passed through to the SupplierPart selection form.
|
|
"""
|
|
order = self.get_order()
|
|
|
|
self.allowed_items = SupplierPart.objects.filter(
|
|
supplier=order.supplier
|
|
).prefetch_related('manufacturer_part')
|
|
|
|
# Fields prefixed with "Part_" can be used to do "smart matching" against Part objects in the database
|
|
q_idx = self.get_column_index('Quantity')
|
|
s_idx = self.get_column_index('Supplier_SKU')
|
|
m_idx = self.get_column_index('Manufacturer_MPN')
|
|
p_idx = self.get_column_index('Purchase_Price')
|
|
r_idx = self.get_column_index('Reference')
|
|
n_idx = self.get_column_index('Notes')
|
|
|
|
for row in self.rows:
|
|
# Initially use a quantity of zero
|
|
quantity = Decimal(0)
|
|
|
|
# Initially we do not have a part to reference
|
|
exact_match_part = None
|
|
|
|
# Check if there is a column corresponding to "quantity"
|
|
if q_idx >= 0:
|
|
q_val = row['data'][q_idx]['cell']
|
|
|
|
if q_val:
|
|
# Delete commas
|
|
q_val = q_val.replace(',', '')
|
|
|
|
try:
|
|
# Attempt to extract a valid quantity from the field
|
|
quantity = Decimal(q_val)
|
|
# Store the 'quantity' value
|
|
row['quantity'] = quantity
|
|
except (ValueError, InvalidOperation):
|
|
pass
|
|
|
|
# Check if there is a column corresponding to "Supplier SKU"
|
|
if s_idx >= 0:
|
|
sku = row['data'][s_idx]['cell']
|
|
|
|
try:
|
|
# Attempt SupplierPart lookup based on SKU value
|
|
exact_match_part = self.allowed_items.get(SKU__contains=sku)
|
|
except (
|
|
ValueError,
|
|
SupplierPart.DoesNotExist,
|
|
SupplierPart.MultipleObjectsReturned,
|
|
):
|
|
exact_match_part = None
|
|
|
|
# Check if there is a column corresponding to "Manufacturer MPN" and no exact match found yet
|
|
if m_idx >= 0 and not exact_match_part:
|
|
mpn = row['data'][m_idx]['cell']
|
|
|
|
try:
|
|
# Attempt SupplierPart lookup based on MPN value
|
|
exact_match_part = self.allowed_items.get(
|
|
manufacturer_part__MPN__contains=mpn
|
|
)
|
|
except (
|
|
ValueError,
|
|
SupplierPart.DoesNotExist,
|
|
SupplierPart.MultipleObjectsReturned,
|
|
):
|
|
exact_match_part = None
|
|
|
|
# Supply list of part options for each row, sorted by how closely they match the part name
|
|
row['item_options'] = self.allowed_items
|
|
|
|
# Unless found, the 'part_match' is blank
|
|
row['item_match'] = None
|
|
|
|
if exact_match_part:
|
|
# If there is an exact match based on SKU or MPN, use that
|
|
row['item_match'] = exact_match_part
|
|
|
|
# Check if there is a column corresponding to "purchase_price"
|
|
if p_idx >= 0:
|
|
p_val = row['data'][p_idx]['cell']
|
|
|
|
if p_val:
|
|
row['purchase_price'] = p_val
|
|
|
|
# Check if there is a column corresponding to "reference"
|
|
if r_idx >= 0:
|
|
reference = row['data'][r_idx]['cell']
|
|
row['reference'] = reference
|
|
|
|
# Check if there is a column corresponding to "notes"
|
|
if n_idx >= 0:
|
|
notes = row['data'][n_idx]['cell']
|
|
row['notes'] = notes
|
|
|
|
def done(self, form_list, **kwargs):
|
|
"""Once all the data is in, process it to add PurchaseOrderLineItem instances to the order."""
|
|
order = self.get_order()
|
|
items = self.get_clean_items()
|
|
|
|
# Create PurchaseOrderLineItem instances
|
|
for purchase_order_item in items.values():
|
|
try:
|
|
supplier_part = SupplierPart.objects.get(
|
|
pk=int(purchase_order_item['part'])
|
|
)
|
|
except (ValueError, SupplierPart.DoesNotExist):
|
|
continue
|
|
|
|
quantity = purchase_order_item.get('quantity', 0)
|
|
if quantity:
|
|
purchase_order_line_item = PurchaseOrderLineItem(
|
|
order=order,
|
|
part=supplier_part,
|
|
quantity=quantity,
|
|
purchase_price=purchase_order_item.get('purchase_price', None),
|
|
reference=purchase_order_item.get('reference', ''),
|
|
notes=purchase_order_item.get('notes', ''),
|
|
)
|
|
try:
|
|
purchase_order_line_item.save()
|
|
except IntegrityError:
|
|
# PurchaseOrderLineItem already exists
|
|
pass
|
|
|
|
return HttpResponseRedirect(
|
|
reverse('po-detail', kwargs={'pk': self.kwargs['pk']})
|
|
)
|
|
|
|
|
|
class SalesOrderExport(AjaxView):
|
|
"""Export a sales order.
|
|
|
|
- File format can optionally be passed as a query parameter e.g. ?format=CSV
|
|
- Default file format is CSV
|
|
"""
|
|
|
|
model = SalesOrder
|
|
|
|
role_required = 'sales_order.view'
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
"""Perform GET request to export SalesOrder dataset."""
|
|
order = get_object_or_404(SalesOrder, pk=self.kwargs.get('pk', None))
|
|
|
|
export_format = request.GET.get('format', 'csv')
|
|
|
|
filename = f'{str(order)} - {order.customer.name}.{export_format}'
|
|
|
|
dataset = SalesOrderLineItemResource().export(queryset=order.lines.all())
|
|
|
|
filedata = dataset.export(format=export_format)
|
|
|
|
return DownloadFile(filedata, filename)
|
|
|
|
|
|
class PurchaseOrderExport(AjaxView):
|
|
"""File download for a purchase order.
|
|
|
|
- File format can be optionally passed as a query param e.g. ?format=CSV
|
|
- Default file format is CSV
|
|
"""
|
|
|
|
model = PurchaseOrder
|
|
|
|
# Specify role as we cannot introspect from "AjaxView"
|
|
role_required = 'purchase_order.view'
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
"""Perform GET request to export PurchaseOrder dataset."""
|
|
order = get_object_or_404(PurchaseOrder, pk=self.kwargs.get('pk', None))
|
|
|
|
export_format = request.GET.get('format', 'csv')
|
|
|
|
filename = f'{str(order)} - {order.supplier.name}.{export_format}'
|
|
|
|
dataset = PurchaseOrderLineItemResource().export(queryset=order.lines.all())
|
|
|
|
filedata = dataset.export(format=export_format)
|
|
|
|
return DownloadFile(filedata, filename)
|
|
|
|
|
|
class LineItemPricing(PartPricing):
|
|
"""View for inspecting part pricing information."""
|
|
|
|
class EnhancedForm(PartPricing.form_class):
|
|
"""Extra form options."""
|
|
|
|
pk = IntegerField(widget=HiddenInput())
|
|
so_line = IntegerField(widget=HiddenInput())
|
|
|
|
form_class = EnhancedForm
|
|
|
|
def get_part(self, id=False):
|
|
"""Return the Part instance associated with this view."""
|
|
if 'line_item' in self.request.GET:
|
|
try:
|
|
part_id = self.request.GET.get('line_item')
|
|
part = SalesOrderLineItem.objects.get(id=part_id).part
|
|
except Part.DoesNotExist:
|
|
return None
|
|
elif 'pk' in self.request.POST:
|
|
try:
|
|
part_id = self.request.POST.get('pk')
|
|
part = Part.objects.get(id=part_id)
|
|
except Part.DoesNotExist:
|
|
return None
|
|
else:
|
|
return None
|
|
|
|
if part and id:
|
|
return part.id
|
|
|
|
return part
|
|
|
|
def get_so(self, pk=False):
|
|
"""Return the SalesOrderLineItem associated with this view."""
|
|
so_line = self.request.GET.get('line_item', None)
|
|
if not so_line:
|
|
so_line = self.request.POST.get('so_line', None)
|
|
|
|
if so_line:
|
|
try:
|
|
sales_order = SalesOrderLineItem.objects.get(pk=so_line)
|
|
if pk:
|
|
return sales_order.pk
|
|
return sales_order
|
|
except Part.DoesNotExist:
|
|
return None
|
|
return None
|
|
|
|
def get_quantity(self):
|
|
"""Return set quantity in decimal format."""
|
|
qty = Decimal(self.request.GET.get('quantity', 1))
|
|
if qty == 1:
|
|
return Decimal(self.request.POST.get('quantity', 1))
|
|
return qty
|
|
|
|
def get_initials(self):
|
|
"""Return initial context values for this view."""
|
|
initials = super().get_initials()
|
|
initials['pk'] = self.get_part(id=True)
|
|
initials['so_line'] = self.get_so(pk=True)
|
|
return initials
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
"""Respond to a POST request to get particular pricing information."""
|
|
REF = 'act-btn_'
|
|
act_btn = [a.replace(REF, '') for a in self.request.POST if REF in a]
|
|
|
|
# check if extra action was passed
|
|
if act_btn and act_btn[0] == 'update_price':
|
|
# get sales order
|
|
so_line = self.get_so()
|
|
if not so_line:
|
|
self.data = {'non_field_errors': [_('Sales order not found')]}
|
|
else:
|
|
quantity = self.get_quantity()
|
|
price = self.get_pricing(quantity).get('unit_part_price', None)
|
|
|
|
if not price:
|
|
self.data = {'non_field_errors': [_('Price not found')]}
|
|
else:
|
|
# set normal update note
|
|
note = _('Updated {part} unit-price to {price}')
|
|
|
|
# check qunatity and update if different
|
|
if so_line.quantity != quantity:
|
|
so_line.quantity = quantity
|
|
note = _(
|
|
'Updated {part} unit-price to {price} and quantity to {qty}'
|
|
)
|
|
|
|
# update sale_price
|
|
so_line.sale_price = price
|
|
so_line.save()
|
|
|
|
# parse response
|
|
data = {
|
|
'form_valid': True,
|
|
'success': note.format(
|
|
part=str(so_line.part),
|
|
price=str(so_line.sale_price),
|
|
qty=quantity,
|
|
),
|
|
}
|
|
return JsonResponse(data=data)
|
|
|
|
# let the normal pricing view run
|
|
return super().post(request, *args, **kwargs)
|