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:
@ -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)
|
||||||
|
|
||||||
|
@ -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(),
|
||||||
|
@ -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)
|
||||||
|
@ -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() {
|
||||||
|
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
@ -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',
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
Reference in New Issue
Block a user