2
0
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:
Oliver Walters
2023-03-15 00:35:37 +11:00
parent acd5dbcac8
commit cd50428685
8 changed files with 80 additions and 18 deletions

View File

@ -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)

View File

@ -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(),

View File

@ -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)

View File

@ -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() {

View File

@ -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;

View File

@ -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);

View File

@ -50,6 +50,9 @@ function returnOrderFields(options={}) {
link: {
icon: 'fa-link',
},
contact: {
icon: 'fa-user',
},
responsible: {
icon: 'fa-user',
}

View File

@ -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