def form_valid(self, form): # if the person was just changed from unaffiliated to an associate member, send them an email. if 'groups' in form.changed_data and 'groups' in form.cleaned_data: oldgroups = form.initial['groups'] newgroups = form.cleaned_data['groups'] if not oldgroups and Group.objects.get( name='Associate') in newgroups: email = DefaultLNLEmailGenerator( subject="Welcome to LNL!", to_emails=[self.object.email], bcc=[settings.EMAIL_TARGET_S], reply_to=[settings.EMAIL_TARGET_S], context={'new_member': self.object}, template_basename='emails/email_welcome') email.send() messages.success(self.request, "Welcome email sent") # Ensure that title is stripped when no longer an officer if Group.objects.get(name="Officer") not in newgroups: self.object.title = None self.object.save() messages.success(self.request, "Account Info Saved!", extra_tags='success') return super(UserUpdateView, self).form_valid(form)
def snipe_checkout(request): if not settings.SNIPE_URL: return HttpResponse('This page is unavailable because SNIPE_URL is not set.', status=501) if not settings.SNIPE_API_KEY: return HttpResponse('This page is unavailable because SNIPE_API_KEY is not set.', status=501) # Get the list of users in the rental group from Snipe error_message = 'Error communicating with Snipe. Did not check out anything.' checkout_to_choices = [] response = requests.request('GET', '{}api/v1/users'.format(settings.SNIPE_URL), headers={ 'authorization': 'Bearer {}'.format(settings.SNIPE_API_KEY), }) if response.status_code == 200: try: data = json.loads(response.text) if data.get('status') == 'error': return HttpResponse(error_message, status=502) checkout_to_choices = [(user['id'], user['name']) for user in data['rows'] if 'rental' in ((group['name'] for group in user['groups']['rows']) if user['groups'] is not None else ())] except ValueError: return HttpResponse(error_message, status=502) else: return HttpResponse(error_message, status=502) # Handle the form error_message = 'Error communicating with Snipe. Some things may have been checked out while some were not. ' \ 'Please go check Snipe.' if request.method == 'POST': receipt_info = {} form = forms.SnipeCheckoutForm(checkout_to_choices, request.POST, request.FILES) if form.is_valid(): success_count_assets = 0 success_count_accessories = 0 for tag in [tag for tag in re.split('[^a-zA-Z0-9]', form.cleaned_data['asset_tags']) if tag]: match = re.match('LNLACC([0-9]+)', tag) if match: tag = match.group(1) # This tag represents an accessory response = requests.request('GET', '{}api/v1/accessories/{}'.format(settings.SNIPE_URL, tag), headers={'authorization': 'Bearer {}'.format(settings.SNIPE_API_KEY), 'accept': 'application/json'}) if response.status_code == 200: try: data = json.loads(response.text) if data.get('status') == 'error': # No accessory with that ID exists in Snipe messages.add_message(request, messages.ERROR, 'No such accessory with ID {}'.format(tag)) continue accessory_name = data['name'] rental_price = float(data['order_number']) if data['order_number'] is not None else None # Check out the accessory response = requests.request('POST', '{}api/v1/accessories/{}/checkout'.format(settings.SNIPE_URL, tag), data=json.dumps({ 'assigned_to': form.cleaned_data['checkout_to'], }), headers={ 'authorization': 'Bearer {}'.format(settings.SNIPE_API_KEY), 'accept': 'application/json', 'content-type': 'application/json', }) if response.status_code == 200: data = json.loads(response.text) if data.get('status') == 'error': # Snipe refused to check out the accessory (maybe they are all checked out) messages.add_message(request, messages.ERROR, 'Unable to check out accessory {}. Snipe says: {}'.format(tag, data['messages'])) continue # The accessory was successfully checked out success_count_accessories += 1 if tag in receipt_info: if receipt_info[tag]['name'] != accessory_name \ or receipt_info[tag]['rental_price'] != rental_price: return HttpResponse(error_message, status=502) receipt_info[tag]['quantity'] += 1 else: receipt_info[tag] = {'name': accessory_name, 'rental_price': rental_price, 'quantity': 1} else: return HttpResponse(error_message, status=502) except ValueError: return HttpResponse(error_message, status=502) else: return HttpResponse(error_message, status=502) else: # This tag represents an asset response = requests.request('GET', '{}api/v1/hardware/bytag/{}'.format(settings.SNIPE_URL, tag), headers={'authorization': 'Bearer {}'.format(settings.SNIPE_API_KEY), 'accept': 'application/json'}) if response.status_code == 200: try: data = json.loads(response.text) if data.get('status') == 'error': # The asset tag does not exist in Snipe messages.add_message(request, messages.ERROR, 'No such asset tag {}'.format(tag)) continue asset_name = data['name'] if 'custom_fields' in data and 'Rental Price' in data['custom_fields'] and \ 'value' in data['custom_fields']['Rental Price'] and data['custom_fields']['Rental Price']['value'] is not None: rental_price = float(data['custom_fields']['Rental Price']['value']) else: rental_price = None # Check out the asset response = requests.request('POST', '{}api/v1/hardware/{}/checkout'.format(settings.SNIPE_URL, data['id']), data=json.dumps({ 'checkout_to_type': 'user', 'assigned_user': form.cleaned_data['checkout_to'], }), headers={ 'authorization': 'Bearer {}'.format(settings.SNIPE_API_KEY), 'accept': 'application/json', 'content-type': 'application/json', }) if response.status_code == 200: data = json.loads(response.text) if data.get('status') == 'error': # Snipe refused to check out the asset (maybe it is already checked out) messages.add_message(request, messages.ERROR, 'Unable to check out asset {} - {}. Snipe says: {}'.format(tag, asset_name, data['messages'])) continue # The asset was successfully checked out success_count_assets += 1 if tag in receipt_info: return HttpResponse(error_message, status=502) receipt_info[tag] = {'name': asset_name, 'rental_price': rental_price, 'quantity': 1} else: return HttpResponse(error_message, status=502) except ValueError: return HttpResponse(error_message, status=502) else: return HttpResponse(error_message, status=502) if success_count_assets > 0 or success_count_accessories > 0: messages.add_message(request, messages.SUCCESS, 'Successfully checked out {} assets and {} accessories'.format(success_count_assets, success_count_accessories)) rental_prices = [(None if asset_info['rental_price'] is None else asset_info['rental_price'] * asset_info['quantity']) for asset_info in receipt_info.values()] total_rental_price = None if None in rental_prices else sum(rental_prices) checkout_to_name = next((item[1] for item in checkout_to_choices if item[0] == form.cleaned_data['checkout_to'])) # Before returning the response, email a PDF receipt html = render_to_string('pdf_templates/inventory-receipt.html', request=request, context={ 'title': 'Checkout Receipt', 'receipt_info': receipt_info, 'num_assets': success_count_assets, 'num_accessories': success_count_accessories, 'total_rental_price': total_rental_price, 'checkout_to': checkout_to_name, }) pdf_file = BytesIO() pisa.CreatePDF(html, dest=pdf_file, link_callback=link_callback) pdf_handle = pdf_file.getvalue() filename = 'LNL-checkout-receipt-{}.pdf'.format(timezone.now().isoformat()) attachments = [{'file_handle': pdf_handle, 'name': filename}] email = DefaultLNLEmailGenerator(subject='LNL Inventory Checkout Receipt', attachments=attachments, to_emails=(request.user.email, settings.DEFAULT_TO_ADDR), body='A receipt for the rental checkout by {} to {} is ' 'attached.'.format(request.user, checkout_to_name)) email.send() # Return the response return render(request, 'inventory/checkout_receipt.html', { 'receipt_info': receipt_info, 'num_assets': success_count_assets, 'num_accessories': success_count_accessories, 'total_rental_price': total_rental_price, 'checkout_to': form.cleaned_data['checkout_to'], 'checkout_to_name': checkout_to_name, }) else: form = forms.SnipeCheckoutForm(checkout_to_choices, initial={'checkout_to': form.cleaned_data['checkout_to']}) else: if 'checkout_to' in request.GET: form = forms.SnipeCheckoutForm(checkout_to_choices, initial={'checkout_to': request.GET['checkout_to']}) else: form = forms.SnipeCheckoutForm(checkout_to_choices) return render(request, "form_crispy.html", { 'msg': 'Inventory checkout', 'form': form, })