mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +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