mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 15:15:42 +00:00 
			
		
		
		
	Merge remote-tracking branch 'inventree/master'
This commit is contained in:
		@@ -44,8 +44,9 @@ class InvenTreeExchange(SimpleExchangeBackend):
 | 
				
			|||||||
            response = urlopen(url, timeout=5, context=context)
 | 
					            response = urlopen(url, timeout=5, context=context)
 | 
				
			||||||
            return response.read()
 | 
					            return response.read()
 | 
				
			||||||
        except Exception:
 | 
					        except Exception:
 | 
				
			||||||
            # Returning None here will raise an error upstream
 | 
					            # Something has gone wrong, but we can just try again next time
 | 
				
			||||||
            return None
 | 
					            # Raise a TypeError so the outer function can handle this
 | 
				
			||||||
 | 
					            raise TypeError
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def update_rates(self, base_currency=None):
 | 
					    def update_rates(self, base_currency=None):
 | 
				
			||||||
        """Set the requested currency codes and get rates."""
 | 
					        """Set the requested currency codes and get rates."""
 | 
				
			||||||
@@ -60,6 +61,8 @@ class InvenTreeExchange(SimpleExchangeBackend):
 | 
				
			|||||||
        # catch connection errors
 | 
					        # catch connection errors
 | 
				
			||||||
        except URLError:
 | 
					        except URLError:
 | 
				
			||||||
            print('Encountered connection error while updating')
 | 
					            print('Encountered connection error while updating')
 | 
				
			||||||
 | 
					        except TypeError:
 | 
				
			||||||
 | 
					            print('Exchange returned invalid response')
 | 
				
			||||||
        except OperationalError as e:
 | 
					        except OperationalError as e:
 | 
				
			||||||
            if 'SerializationFailure' in e.__cause__.__class__.__name__:
 | 
					            if 'SerializationFailure' in e.__cause__.__class__.__name__:
 | 
				
			||||||
                print('Serialization Failure while updating exchange rates')
 | 
					                print('Serialization Failure while updating exchange rates')
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								InvenTree/InvenTree/static/script/qr-scanner.min.js.map
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								InvenTree/InvenTree/static/script/qr-scanner.min.js.map
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							@@ -32,6 +32,7 @@ import InvenTree.tasks
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from plugin.events import trigger_event
 | 
					from plugin.events import trigger_event
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import common.notifications
 | 
				
			||||||
from part import models as PartModels
 | 
					from part import models as PartModels
 | 
				
			||||||
from stock import models as StockModels
 | 
					from stock import models as StockModels
 | 
				
			||||||
from users import models as UserModels
 | 
					from users import models as UserModels
 | 
				
			||||||
@@ -534,12 +535,51 @@ class Build(MPTTModel, ReferenceIndexingMixin):
 | 
				
			|||||||
        self.subtract_allocated_stock(user)
 | 
					        self.subtract_allocated_stock(user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Ensure that there are no longer any BuildItem objects
 | 
					        # Ensure that there are no longer any BuildItem objects
 | 
				
			||||||
        # which point to thisFcan Build Order
 | 
					        # which point to this Build Order
 | 
				
			||||||
        self.allocated_stock.all().delete()
 | 
					        self.allocated_stock.all().delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Register an event
 | 
					        # Register an event
 | 
				
			||||||
        trigger_event('build.completed', id=self.pk)
 | 
					        trigger_event('build.completed', id=self.pk)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Notify users that this build has been completed
 | 
				
			||||||
 | 
					        targets = [
 | 
				
			||||||
 | 
					            self.issued_by,
 | 
				
			||||||
 | 
					            self.responsible,
 | 
				
			||||||
 | 
					        ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Notify those users interested in the parent build
 | 
				
			||||||
 | 
					        if self.parent:
 | 
				
			||||||
 | 
					            targets.append(self.parent.issued_by)
 | 
				
			||||||
 | 
					            targets.append(self.parent.responsible)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Notify users if this build points to a sales order
 | 
				
			||||||
 | 
					        if self.sales_order:
 | 
				
			||||||
 | 
					            targets.append(self.sales_order.created_by)
 | 
				
			||||||
 | 
					            targets.append(self.sales_order.responsible)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        build = self
 | 
				
			||||||
 | 
					        name = _(f'Build order {build} has been completed')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        context = {
 | 
				
			||||||
 | 
					            'build': build,
 | 
				
			||||||
 | 
					            'name': name,
 | 
				
			||||||
 | 
					            'slug': 'build.completed',
 | 
				
			||||||
 | 
					            'message': _('A build order has been completed'),
 | 
				
			||||||
 | 
					            'link': InvenTree.helpers.construct_absolute_url(self.get_absolute_url()),
 | 
				
			||||||
 | 
					            'template': {
 | 
				
			||||||
 | 
					                'html': 'email/build_order_completed.html',
 | 
				
			||||||
 | 
					                'subject': name,
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        common.notifications.trigger_notification(
 | 
				
			||||||
 | 
					            build,
 | 
				
			||||||
 | 
					            'build.completed',
 | 
				
			||||||
 | 
					            targets=targets,
 | 
				
			||||||
 | 
					            context=context,
 | 
				
			||||||
 | 
					            target_exclude=[user],
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @transaction.atomic
 | 
					    @transaction.atomic
 | 
				
			||||||
    def cancel_build(self, user, **kwargs):
 | 
					    def cancel_build(self, user, **kwargs):
 | 
				
			||||||
        """Mark the Build as CANCELLED.
 | 
					        """Mark the Build as CANCELLED.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -291,7 +291,7 @@ class InvenTreeNotificationBodies:
 | 
				
			|||||||
    NewOrder = NotificationBody(
 | 
					    NewOrder = NotificationBody(
 | 
				
			||||||
        name=_("New {verbose_name}"),
 | 
					        name=_("New {verbose_name}"),
 | 
				
			||||||
        slug='{app_label}.new_{model_name}',
 | 
					        slug='{app_label}.new_{model_name}',
 | 
				
			||||||
        message=_("A new {verbose_name} has been created and ,assigned to you"),
 | 
					        message=_("A new order has been created and assigned to you"),
 | 
				
			||||||
        template='email/new_order_assigned.html',
 | 
					        template='email/new_order_assigned.html',
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    """Send when a new order (build, sale or purchase) was created."""
 | 
					    """Send when a new order (build, sale or purchase) was created."""
 | 
				
			||||||
@@ -344,8 +344,10 @@ def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if targets:
 | 
					    if targets:
 | 
				
			||||||
        for target in targets:
 | 
					        for target in targets:
 | 
				
			||||||
 | 
					            if target is None:
 | 
				
			||||||
 | 
					                continue
 | 
				
			||||||
            # User instance is provided
 | 
					            # User instance is provided
 | 
				
			||||||
            if isinstance(target, get_user_model()):
 | 
					            elif isinstance(target, get_user_model()):
 | 
				
			||||||
                if target not in target_exclude:
 | 
					                if target not in target_exclude:
 | 
				
			||||||
                    target_users.add(target)
 | 
					                    target_users.add(target)
 | 
				
			||||||
            # Group instance is provided
 | 
					            # Group instance is provided
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										27
									
								
								InvenTree/templates/email/build_order_completed.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								InvenTree/templates/email/build_order_completed.html
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,27 @@
 | 
				
			|||||||
 | 
					{% extends "email/email.html" %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% load i18n %}
 | 
				
			||||||
 | 
					{% load inventree_extras %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block title %}
 | 
				
			||||||
 | 
					{{ message }}
 | 
				
			||||||
 | 
					{% if link %}
 | 
				
			||||||
 | 
					<p>{% trans "Click on the following link to view this order" %}: <a href="{{ link }}">{{ link }}</a></p>
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
 | 
					{% endblock title %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% block body %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<tr style="height: 3rem; border-bottom: 1px solid">
 | 
				
			||||||
 | 
					    <th>{% trans "Build Order" %}</th>
 | 
				
			||||||
 | 
					    <th>{% trans "Part" %}</th>
 | 
				
			||||||
 | 
					    <th>{% trans "Quantity" %}</th>
 | 
				
			||||||
 | 
					</tr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<tr style="height: 3rem">
 | 
				
			||||||
 | 
					    <td style="text-align: center;">{{ build }}</td>
 | 
				
			||||||
 | 
					    <td style="text-align: center;">{{ build.part.full_name }}</td>
 | 
				
			||||||
 | 
					    <td style="text-align: center;">{{ build.quantity }}</td>
 | 
				
			||||||
 | 
					</tr>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					{% endblock body %}
 | 
				
			||||||
@@ -938,7 +938,7 @@ function getFormFieldElement(name, options) {
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
 * Check that a "numerical" input field has a valid number in it.
 | 
					 * Check that a "numerical" input field has a valid number in it.
 | 
				
			||||||
 * An invalid number is expunged at the client side by the getFormFieldValue() function,
 | 
					 * An invalid number is expunged at the client side by the getFormFieldValue() function,
 | 
				
			||||||
 * which means that an empty string '' is sent to the server if the number is not valud.
 | 
					 * which means that an empty string '' is sent to the server if the number is not valid.
 | 
				
			||||||
 * This can result in confusing error messages displayed under the form field.
 | 
					 * This can result in confusing error messages displayed under the form field.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * So, we can invalid numbers and display errors *before* the form is submitted!
 | 
					 * So, we can invalid numbers and display errors *before* the form is submitted!
 | 
				
			||||||
@@ -947,7 +947,8 @@ function validateFormField(name, options) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    if (getFormFieldElement(name, options)) {
 | 
					    if (getFormFieldElement(name, options)) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        var el = document.getElementById(`id_${name}`);
 | 
					        var field_name = getFieldName(name, options);
 | 
				
			||||||
 | 
					        var el = document.getElementById(`id_${field_name}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (el.validity.valueMissing) {
 | 
					        if (el.validity.valueMissing) {
 | 
				
			||||||
            // Accept empty strings (server will validate)
 | 
					            // Accept empty strings (server will validate)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,18 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Trim the supplied string to ensure the string length is limited to the provided value
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					function trim(data, max_length=100) {
 | 
				
			||||||
 | 
					    if (data.length > max_length) {
 | 
				
			||||||
 | 
					        data = data.slice(0, max_length - 3) + '...';
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Should the ID be rendered for this string
 | 
					// Should the ID be rendered for this string
 | 
				
			||||||
function renderId(title, pk, parameters={}) {
 | 
					function renderId(title, pk, parameters={}) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -55,7 +67,7 @@ function renderCompany(name, data, parameters={}, options={}) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    var html = select2Thumbnail(data.image);
 | 
					    var html = select2Thumbnail(data.image);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html += `<span><b>${data.name}</b></span> - <i>${data.description}</i>`;
 | 
					    html += `<span><b>${data.name}</b></span> - <i>${trim(data.description)}</i>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html += renderId('{% trans "Company ID" %}', data.pk, parameters);
 | 
					    html += renderId('{% trans "Company ID" %}', data.pk, parameters);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -141,7 +153,7 @@ function renderStockLocation(name, data, parameters={}, options={}) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (render_description && data.description) {
 | 
					    if (render_description && data.description) {
 | 
				
			||||||
        html += ` - <i>${data.description}</i>`;
 | 
					        html += ` - <i>${trim(data.description)}</i>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html += renderId('{% trans "Location ID" %}', data.pk, parameters);
 | 
					    html += renderId('{% trans "Location ID" %}', data.pk, parameters);
 | 
				
			||||||
@@ -177,7 +189,7 @@ function renderPart(name, data, parameters={}, options={}) {
 | 
				
			|||||||
    html += ` <span>${data.full_name || data.name}</span>`;
 | 
					    html += ` <span>${data.full_name || data.name}</span>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (data.description) {
 | 
					    if (data.description) {
 | 
				
			||||||
        html += ` - <i><small>${data.description}</small></i>`;
 | 
					        html += ` - <i><small>${trim(data.description)}</small></i>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var stock_data = '';
 | 
					    var stock_data = '';
 | 
				
			||||||
@@ -256,7 +268,7 @@ function renderPurchaseOrder(name, data, parameters={}, options={}) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (data.description) {
 | 
					    if (data.description) {
 | 
				
			||||||
        html += ` - <em>${data.description}</em>`;
 | 
					        html += ` - <em>${trim(data.description)}</em>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html += renderId('{% trans "Order ID" %}', data.pk, parameters);
 | 
					    html += renderId('{% trans "Order ID" %}', data.pk, parameters);
 | 
				
			||||||
@@ -282,7 +294,7 @@ function renderSalesOrder(name, data, parameters={}, options={}) {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (data.description) {
 | 
					    if (data.description) {
 | 
				
			||||||
        html += ` - <em>${data.description}</em>`;
 | 
					        html += ` - <em>${trim(data.description)}</em>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html += renderId('{% trans "Order ID" %}', data.pk, parameters);
 | 
					    html += renderId('{% trans "Order ID" %}', data.pk, parameters);
 | 
				
			||||||
@@ -319,7 +331,7 @@ function renderPartCategory(name, data, parameters={}, options={}) {
 | 
				
			|||||||
    var html = `<span>${level}${data.pathstring}</span>`;
 | 
					    var html = `<span>${level}${data.pathstring}</span>`;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (data.description) {
 | 
					    if (data.description) {
 | 
				
			||||||
        html += ` - <i>${data.description}</i>`;
 | 
					        html += ` - <i>${trim(data.description)}</i>`;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    html += renderId('{% trans "Category ID" %}', data.pk, parameters);
 | 
					    html += renderId('{% trans "Category ID" %}', data.pk, parameters);
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user