2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-16 20:15:44 +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 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', company = models.ForeignKey(Company, related_name='contacts',
on_delete=models.CASCADE) on_delete=models.CASCADE)

View File

@ -170,6 +170,18 @@ class Order(MetadataMixin, ReferenceIndexingMixin):
super().save(*args, **kwargs) 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')) 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')) 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') 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")) supplier_reference = models.CharField(max_length=64, blank=True, verbose_name=_('Supplier Reference'), help_text=_("Supplier order reference code"))
received_by = models.ForeignKey( received_by = models.ForeignKey(
@ -711,6 +728,11 @@ class SalesOrder(TotalPriceMixin, Order):
help_text=_("Company to which the items are being sold"), 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( status = models.PositiveIntegerField(
default=SalesOrderStatus.PENDING, default=SalesOrderStatus.PENDING,
choices=SalesOrderStatus.items(), choices=SalesOrderStatus.items(),
@ -1626,6 +1648,11 @@ class ReturnOrder(Order):
help_text=_("Company from which items are being returned"), 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( status = models.PositiveIntegerField(
default=ReturnOrderStatus.PENDING, default=ReturnOrderStatus.PENDING,
choices=ReturnOrderStatus.items(), choices=ReturnOrderStatus.items(),

View File

@ -17,7 +17,8 @@ import order.models
import part.filters import part.filters
import stock.models import stock.models
import stock.serializers 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.helpers import extract_serial_numbers, normalize, str2bool
from InvenTree.serializers import (InvenTreeAttachmentSerializer, from InvenTree.serializers import (InvenTreeAttachmentSerializer,
InvenTreeCurrencySerializer, InvenTreeCurrencySerializer,
@ -1411,6 +1412,8 @@ class ReturnOrderSerializer(InvenTreeModelSerializer):
fields = [ fields = [
'pk', 'pk',
'creation_date', 'creation_date',
'contact',
'contact_detail',
'customer', 'customer',
'customer_detail', 'customer_detail',
'customer_reference', 'customer_reference',
@ -1446,6 +1449,8 @@ class ReturnOrderSerializer(InvenTreeModelSerializer):
# TODO # TODO
return queryset return queryset
contact_detail = ContactSerializer(source='contact', many=False, read_only=True)
customer_detail = CompanyBriefSerializer(source='customer', 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) 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 js_ready %}
{{ block.super }} {{ block.super }}
{% if roles.sales_order.change %}
$("#edit-order").click(function() { $("#edit-order").click(function() {
editSalesOrder({{ order.pk }}, { editSalesOrder({{ order.pk }}, {
reload: true, reload: true,
}); });
}); });
{% endif %}
$("#complete-order-shipments").click(function() { $("#complete-order-shipments").click(function() {

View File

@ -2044,6 +2044,9 @@ function renderModelData(name, model, data, parameters, options) {
case 'company': case 'company':
renderer = renderCompany; renderer = renderCompany;
break; break;
case 'contact':
renderer = renderContact;
break;
case 'stockitem': case 'stockitem':
renderer = renderStockItem; renderer = renderStockItem;
break; break;

View File

@ -8,6 +8,7 @@
/* exported /* exported
renderBuild, renderBuild,
renderCompany, renderCompany,
renderContact,
renderGroup, renderGroup,
renderManufacturerPart, renderManufacturerPart,
renderOwner, renderOwner,
@ -67,7 +68,7 @@ function renderId(title, pk, parameters={}) {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function renderCompany(name, data, parameters={}, options={}) { 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>`; 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 // Renderer for "StockItem" model
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function renderStockItem(name, data, parameters={}, options={}) { function renderStockItem(name, data, parameters={}, options={}) {
@ -138,7 +154,7 @@ function renderStockItem(name, data, parameters={}, options={}) {
} }
} }
var html = ` let html = `
<span> <span>
${part_detail} ${part_detail}
${stock_detail} ${stock_detail}
@ -157,7 +173,7 @@ function renderStockLocation(name, data, parameters={}, options={}) {
var level = '- '.repeat(data.level); var level = '- '.repeat(data.level);
var html = `<span>${level}${data.pathstring}</span>`; let html = `<span>${level}${data.pathstring}</span>`;
var render_description = true; var render_description = true;
@ -183,7 +199,7 @@ function renderBuild(name, data, parameters={}, options={}) {
image = data.part_detail.thumbnail; 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>`; 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 // eslint-disable-next-line no-unused-vars
function renderPart(name, data, parameters={}, options={}) { function renderPart(name, data, parameters={}, options={}) {
var html = select2Thumbnail(data.image); let html = select2Thumbnail(data.image);
html += ` <span>${data.full_name || data.name}</span>`; 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 // eslint-disable-next-line no-unused-vars
function renderGroup(name, data, parameters={}, options={}) { function renderGroup(name, data, parameters={}, options={}) {
var html = `<span>${data.name}</span>`; let html = `<span>${data.name}</span>`;
return html; return html;
@ -244,7 +260,7 @@ function renderGroup(name, data, parameters={}, options={}) {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function renderUser(name, data, parameters={}, options={}) { 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) { if (data.first_name && data.last_name) {
html += ` - <i>${data.first_name} ${data.last_name}</i>`; 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 // eslint-disable-next-line no-unused-vars
function renderOwner(name, data, parameters={}, options={}) { function renderOwner(name, data, parameters={}, options={}) {
var html = `<span>${data.name}</span>`; let html = `<span>${data.name}</span>`;
switch (data.label) { switch (data.label) {
case 'user': case 'user':
@ -279,7 +295,7 @@ function renderOwner(name, data, parameters={}, options={}) {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function renderPurchaseOrder(name, data, parameters={}, options={}) { function renderPurchaseOrder(name, data, parameters={}, options={}) {
var html = ''; let html = '';
if (data.supplier_detail) { if (data.supplier_detail) {
thumbnail = data.supplier_detail.thumbnail || data.supplier_detail.image; 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 // eslint-disable-next-line no-unused-vars
function renderSalesOrder(name, data, parameters={}, options={}) { function renderSalesOrder(name, data, parameters={}, options={}) {
var html = `<span>${data.reference}</span>`; let html = `<span>${data.reference}</span>`;
var thumbnail = null; var thumbnail = null;
@ -334,7 +350,7 @@ function renderSalesOrder(name, data, parameters={}, options={}) {
// eslint-disable-next-line no-unused-vars // eslint-disable-next-line no-unused-vars
function renderSalesOrderShipment(name, data, parameters={}, options={}) { function renderSalesOrderShipment(name, data, parameters={}, options={}) {
var html = ` let html = `
<span>${data.order_detail.reference} - {% trans "Shipment" %} ${data.reference}</span> <span>${data.order_detail.reference} - {% trans "Shipment" %} ${data.reference}</span>
<span class='float-right'> <span class='float-right'>
<small>{% trans "Shipment ID" %}: ${data.pk}</small> <small>{% trans "Shipment ID" %}: ${data.pk}</small>
@ -353,7 +369,7 @@ function renderPartCategory(name, data, parameters={}, options={}) {
var level = '- '.repeat(data.level); var level = '- '.repeat(data.level);
var html = `<span>${level}${data.pathstring}</span>`; let html = `<span>${level}${data.pathstring}</span>`;
if (data.description) { if (data.description) {
html += ` - <i>${trim(data.description)}</i>`; html += ` - <i>${trim(data.description)}</i>`;
@ -373,7 +389,7 @@ function renderPartParameterTemplate(name, data, parameters={}, options={}) {
units = ` [${data.units}]`; units = ` [${data.units}]`;
} }
var html = `<span>${data.name}${units}</span>`; let html = `<span>${data.name}${units}</span>`;
return html; return html;
} }
@ -394,7 +410,7 @@ function renderManufacturerPart(name, data, parameters={}, options={}) {
part_image = data.part_detail.thumbnail || data.part_detail.image; part_image = data.part_detail.thumbnail || data.part_detail.image;
} }
var html = ''; let html = '';
html += select2Thumbnail(manufacturer_image); html += select2Thumbnail(manufacturer_image);
html += select2Thumbnail(part_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; part_image = data.part_detail.thumbnail || data.part_detail.image;
} }
var html = ''; let html = '';
html += select2Thumbnail(supplier_image); html += select2Thumbnail(supplier_image);

View File

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

View File

@ -136,6 +136,7 @@ class RuleSet(models.Model):
'purchase_order': [ 'purchase_order': [
'company_company', 'company_company',
'company_companyattachment', 'company_companyattachment',
'company_contact',
'company_manufacturerpart', 'company_manufacturerpart',
'company_manufacturerpartparameter', 'company_manufacturerpartparameter',
'company_supplierpart', 'company_supplierpart',
@ -149,6 +150,7 @@ class RuleSet(models.Model):
'sales_order': [ 'sales_order': [
'company_company', 'company_company',
'company_companyattachment', 'company_companyattachment',
'company_contact',
'order_salesorder', 'order_salesorder',
'order_salesorderallocation', 'order_salesorderallocation',
'order_salesorderattachment', 'order_salesorderattachment',
@ -160,6 +162,7 @@ class RuleSet(models.Model):
'return_order': [ 'return_order': [
'company_company', 'company_company',
'company_companyattachment', 'company_companyattachment',
'company_contact',
'order_returnorder', 'order_returnorder',
'order_returnorderlineitem', 'order_returnorderlineitem',
'order_returnorderextraline', 'order_returnorderextraline',
@ -181,7 +184,6 @@ class RuleSet(models.Model):
'common_webhookmessage', 'common_webhookmessage',
'common_notificationentry', 'common_notificationentry',
'common_notificationmessage', 'common_notificationmessage',
'company_contact',
'users_owner', 'users_owner',
# Third-party tables # Third-party tables