mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-14 19:15:41 +00:00
Adds "contact" to ReturnOrder
- Implement check to ensure that the selected "contact" matches the selected "company"
This commit is contained in:
@ -235,6 +235,11 @@ class Contact(models.Model):
|
||||
role: position in company
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def get_api_url():
|
||||
"""Return the API URL associated with the Contcat model"""
|
||||
return reverse('api-contact-list')
|
||||
|
||||
company = models.ForeignKey(Company, related_name='contacts',
|
||||
on_delete=models.CASCADE)
|
||||
|
||||
|
@ -170,6 +170,18 @@ class Order(MetadataMixin, ReferenceIndexingMixin):
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def clean(self):
|
||||
"""Custom clean method for the generic order class"""
|
||||
|
||||
super().clean()
|
||||
|
||||
# Check that the referenced 'contact' matches the correct 'company'
|
||||
if self.company and self.contact:
|
||||
if self.contact.company != self.company:
|
||||
raise ValidationError({
|
||||
"contact": _("Contact does not match selected company")
|
||||
})
|
||||
|
||||
description = models.CharField(max_length=250, verbose_name=_('Description'), help_text=_('Order description'))
|
||||
|
||||
link = InvenTreeURLField(blank=True, verbose_name=_('Link'), help_text=_('Link to external page'))
|
||||
@ -305,6 +317,11 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
||||
help_text=_('Company from which the items are being ordered')
|
||||
)
|
||||
|
||||
@property
|
||||
def company(self):
|
||||
"""Accessor helper for Order base class"""
|
||||
return self.suplier
|
||||
|
||||
supplier_reference = models.CharField(max_length=64, blank=True, verbose_name=_('Supplier Reference'), help_text=_("Supplier order reference code"))
|
||||
|
||||
received_by = models.ForeignKey(
|
||||
@ -711,6 +728,11 @@ class SalesOrder(TotalPriceMixin, Order):
|
||||
help_text=_("Company to which the items are being sold"),
|
||||
)
|
||||
|
||||
@property
|
||||
def company(self):
|
||||
"""Accessor helper for Order base"""
|
||||
return self.customer
|
||||
|
||||
status = models.PositiveIntegerField(
|
||||
default=SalesOrderStatus.PENDING,
|
||||
choices=SalesOrderStatus.items(),
|
||||
@ -1626,6 +1648,11 @@ class ReturnOrder(Order):
|
||||
help_text=_("Company from which items are being returned"),
|
||||
)
|
||||
|
||||
@property
|
||||
def company(self):
|
||||
"""Accessor helper for Order base class"""
|
||||
return self.customer
|
||||
|
||||
status = models.PositiveIntegerField(
|
||||
default=ReturnOrderStatus.PENDING,
|
||||
choices=ReturnOrderStatus.items(),
|
||||
|
@ -17,7 +17,8 @@ import order.models
|
||||
import part.filters
|
||||
import stock.models
|
||||
import stock.serializers
|
||||
from company.serializers import CompanyBriefSerializer, SupplierPartSerializer
|
||||
from company.serializers import (CompanyBriefSerializer, ContactSerializer,
|
||||
SupplierPartSerializer)
|
||||
from InvenTree.helpers import extract_serial_numbers, normalize, str2bool
|
||||
from InvenTree.serializers import (InvenTreeAttachmentSerializer,
|
||||
InvenTreeCurrencySerializer,
|
||||
@ -1411,6 +1412,8 @@ class ReturnOrderSerializer(InvenTreeModelSerializer):
|
||||
fields = [
|
||||
'pk',
|
||||
'creation_date',
|
||||
'contact',
|
||||
'contact_detail',
|
||||
'customer',
|
||||
'customer_detail',
|
||||
'customer_reference',
|
||||
@ -1446,6 +1449,8 @@ class ReturnOrderSerializer(InvenTreeModelSerializer):
|
||||
# TODO
|
||||
return queryset
|
||||
|
||||
contact_detail = ContactSerializer(source='contact', many=False, read_only=True)
|
||||
|
||||
customer_detail = CompanyBriefSerializer(source='customer', many=False, read_only=True)
|
||||
|
||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||
|
@ -204,12 +204,13 @@ src="{% static 'img/blank_image.png' %}"
|
||||
{% block js_ready %}
|
||||
{{ block.super }}
|
||||
|
||||
{% if roles.sales_order.change %}
|
||||
$("#edit-order").click(function() {
|
||||
|
||||
editSalesOrder({{ order.pk }}, {
|
||||
reload: true,
|
||||
});
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
$("#complete-order-shipments").click(function() {
|
||||
|
||||
|
@ -2044,6 +2044,9 @@ function renderModelData(name, model, data, parameters, options) {
|
||||
case 'company':
|
||||
renderer = renderCompany;
|
||||
break;
|
||||
case 'contact':
|
||||
renderer = renderContact;
|
||||
break;
|
||||
case 'stockitem':
|
||||
renderer = renderStockItem;
|
||||
break;
|
||||
|
@ -8,6 +8,7 @@
|
||||
/* exported
|
||||
renderBuild,
|
||||
renderCompany,
|
||||
renderContact,
|
||||
renderGroup,
|
||||
renderManufacturerPart,
|
||||
renderOwner,
|
||||
@ -67,7 +68,7 @@ function renderId(title, pk, parameters={}) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderCompany(name, data, parameters={}, options={}) {
|
||||
|
||||
var html = select2Thumbnail(data.image);
|
||||
let html = select2Thumbnail(data.image);
|
||||
|
||||
html += `<span><b>${data.name}</b></span> - <i>${trim(data.description)}</i>`;
|
||||
|
||||
@ -77,6 +78,21 @@ function renderCompany(name, data, parameters={}, options={}) {
|
||||
}
|
||||
|
||||
|
||||
// Renderer for the "Contact" model
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderContact(name, data, parameters={}, options={}) {
|
||||
let html = `<span><b>${data.name}</b>`;
|
||||
|
||||
if (data.email) {
|
||||
html += `- <i>${data.email}</i>`;
|
||||
|
||||
html += renderId('{% trans "Contact ID" %}', data.pk, parameters);
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "StockItem" model
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderStockItem(name, data, parameters={}, options={}) {
|
||||
@ -138,7 +154,7 @@ function renderStockItem(name, data, parameters={}, options={}) {
|
||||
}
|
||||
}
|
||||
|
||||
var html = `
|
||||
let html = `
|
||||
<span>
|
||||
${part_detail}
|
||||
${stock_detail}
|
||||
@ -157,7 +173,7 @@ function renderStockLocation(name, data, parameters={}, options={}) {
|
||||
|
||||
var level = '- '.repeat(data.level);
|
||||
|
||||
var html = `<span>${level}${data.pathstring}</span>`;
|
||||
let html = `<span>${level}${data.pathstring}</span>`;
|
||||
|
||||
var render_description = true;
|
||||
|
||||
@ -183,7 +199,7 @@ function renderBuild(name, data, parameters={}, options={}) {
|
||||
image = data.part_detail.thumbnail;
|
||||
}
|
||||
|
||||
var html = select2Thumbnail(image);
|
||||
let html = select2Thumbnail(image);
|
||||
|
||||
html += `<span><b>${data.reference}</b> - ${data.quantity} x ${data.part_detail.full_name}</span>`;
|
||||
|
||||
@ -197,7 +213,7 @@ function renderBuild(name, data, parameters={}, options={}) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderPart(name, data, parameters={}, options={}) {
|
||||
|
||||
var html = select2Thumbnail(data.image);
|
||||
let html = select2Thumbnail(data.image);
|
||||
|
||||
html += ` <span>${data.full_name || data.name}</span>`;
|
||||
|
||||
@ -234,7 +250,7 @@ function renderPart(name, data, parameters={}, options={}) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderGroup(name, data, parameters={}, options={}) {
|
||||
|
||||
var html = `<span>${data.name}</span>`;
|
||||
let html = `<span>${data.name}</span>`;
|
||||
|
||||
return html;
|
||||
|
||||
@ -244,7 +260,7 @@ function renderGroup(name, data, parameters={}, options={}) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderUser(name, data, parameters={}, options={}) {
|
||||
|
||||
var html = `<span>${data.username}</span>`;
|
||||
let html = `<span>${data.username}</span>`;
|
||||
|
||||
if (data.first_name && data.last_name) {
|
||||
html += ` - <i>${data.first_name} ${data.last_name}</i>`;
|
||||
@ -258,7 +274,7 @@ function renderUser(name, data, parameters={}, options={}) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderOwner(name, data, parameters={}, options={}) {
|
||||
|
||||
var html = `<span>${data.name}</span>`;
|
||||
let html = `<span>${data.name}</span>`;
|
||||
|
||||
switch (data.label) {
|
||||
case 'user':
|
||||
@ -279,7 +295,7 @@ function renderOwner(name, data, parameters={}, options={}) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderPurchaseOrder(name, data, parameters={}, options={}) {
|
||||
|
||||
var html = '';
|
||||
let html = '';
|
||||
|
||||
if (data.supplier_detail) {
|
||||
thumbnail = data.supplier_detail.thumbnail || data.supplier_detail.image;
|
||||
@ -309,7 +325,7 @@ function renderPurchaseOrder(name, data, parameters={}, options={}) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderSalesOrder(name, data, parameters={}, options={}) {
|
||||
|
||||
var html = `<span>${data.reference}</span>`;
|
||||
let html = `<span>${data.reference}</span>`;
|
||||
|
||||
var thumbnail = null;
|
||||
|
||||
@ -334,7 +350,7 @@ function renderSalesOrder(name, data, parameters={}, options={}) {
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderSalesOrderShipment(name, data, parameters={}, options={}) {
|
||||
|
||||
var html = `
|
||||
let html = `
|
||||
<span>${data.order_detail.reference} - {% trans "Shipment" %} ${data.reference}</span>
|
||||
<span class='float-right'>
|
||||
<small>{% trans "Shipment ID" %}: ${data.pk}</small>
|
||||
@ -353,7 +369,7 @@ function renderPartCategory(name, data, parameters={}, options={}) {
|
||||
|
||||
var level = '- '.repeat(data.level);
|
||||
|
||||
var html = `<span>${level}${data.pathstring}</span>`;
|
||||
let html = `<span>${level}${data.pathstring}</span>`;
|
||||
|
||||
if (data.description) {
|
||||
html += ` - <i>${trim(data.description)}</i>`;
|
||||
@ -373,7 +389,7 @@ function renderPartParameterTemplate(name, data, parameters={}, options={}) {
|
||||
units = ` [${data.units}]`;
|
||||
}
|
||||
|
||||
var html = `<span>${data.name}${units}</span>`;
|
||||
let html = `<span>${data.name}${units}</span>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
@ -394,7 +410,7 @@ function renderManufacturerPart(name, data, parameters={}, options={}) {
|
||||
part_image = data.part_detail.thumbnail || data.part_detail.image;
|
||||
}
|
||||
|
||||
var html = '';
|
||||
let html = '';
|
||||
|
||||
html += select2Thumbnail(manufacturer_image);
|
||||
html += select2Thumbnail(part_image);
|
||||
@ -423,7 +439,7 @@ function renderSupplierPart(name, data, parameters={}, options={}) {
|
||||
part_image = data.part_detail.thumbnail || data.part_detail.image;
|
||||
}
|
||||
|
||||
var html = '';
|
||||
let html = '';
|
||||
|
||||
html += select2Thumbnail(supplier_image);
|
||||
|
||||
|
@ -50,6 +50,9 @@ function returnOrderFields(options={}) {
|
||||
link: {
|
||||
icon: 'fa-link',
|
||||
},
|
||||
contact: {
|
||||
icon: 'fa-user',
|
||||
},
|
||||
responsible: {
|
||||
icon: 'fa-user',
|
||||
}
|
||||
|
@ -136,6 +136,7 @@ class RuleSet(models.Model):
|
||||
'purchase_order': [
|
||||
'company_company',
|
||||
'company_companyattachment',
|
||||
'company_contact',
|
||||
'company_manufacturerpart',
|
||||
'company_manufacturerpartparameter',
|
||||
'company_supplierpart',
|
||||
@ -149,6 +150,7 @@ class RuleSet(models.Model):
|
||||
'sales_order': [
|
||||
'company_company',
|
||||
'company_companyattachment',
|
||||
'company_contact',
|
||||
'order_salesorder',
|
||||
'order_salesorderallocation',
|
||||
'order_salesorderattachment',
|
||||
@ -160,6 +162,7 @@ class RuleSet(models.Model):
|
||||
'return_order': [
|
||||
'company_company',
|
||||
'company_companyattachment',
|
||||
'company_contact',
|
||||
'order_returnorder',
|
||||
'order_returnorderlineitem',
|
||||
'order_returnorderextraline',
|
||||
@ -181,7 +184,6 @@ class RuleSet(models.Model):
|
||||
'common_webhookmessage',
|
||||
'common_notificationentry',
|
||||
'common_notificationmessage',
|
||||
'company_contact',
|
||||
'users_owner',
|
||||
|
||||
# Third-party tables
|
||||
|
Reference in New Issue
Block a user