mirror of
https://github.com/inventree/InvenTree.git
synced 2026-04-12 06:18:48 +00:00
697 lines
24 KiB
Python
697 lines
24 KiB
Python
"""Unit testing for the various report models."""
|
|
|
|
from io import StringIO
|
|
from zoneinfo import ZoneInfo
|
|
|
|
from django.apps import apps
|
|
from django.conf import settings
|
|
from django.core.cache import cache
|
|
from django.test import TestCase, override_settings
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
from django.utils.safestring import SafeString
|
|
|
|
from PIL import Image
|
|
|
|
import report.models as report_models
|
|
from build.models import Build
|
|
from common.models import Attachment, InvenTreeSetting
|
|
from InvenTree.unit_test import AdminTestCase, InvenTreeAPITestCase
|
|
from order.models import ReturnOrder, SalesOrder
|
|
from part.models import Part
|
|
from plugin.registry import registry
|
|
from report.models import LabelTemplate, ReportTemplate
|
|
from report.templatetags import barcode as barcode_tags
|
|
from report.templatetags import report as report_tags
|
|
from stock.models import StockItem
|
|
|
|
|
|
class ReportTagTest(TestCase):
|
|
"""Unit tests for the report template tags."""
|
|
|
|
def debug_mode(self, value: bool):
|
|
"""Enable or disable debug mode for reports."""
|
|
InvenTreeSetting.set_setting('REPORT_DEBUG_MODE', value, change_user=None)
|
|
|
|
def test_getindex(self):
|
|
"""Tests for the 'getindex' template tag."""
|
|
fn = report_tags.getindex
|
|
data = [1, 2, 3, 4, 5, 6]
|
|
|
|
# Out of bounds or invalid
|
|
self.assertEqual(fn(data, -1), None)
|
|
self.assertEqual(fn(data, 99), None)
|
|
self.assertEqual(fn(data, 'xx'), None)
|
|
|
|
for idx in range(len(data)):
|
|
self.assertEqual(fn(data, idx), data[idx])
|
|
|
|
def test_getkey(self):
|
|
"""Tests for the 'getkey' template tag."""
|
|
data = {'hello': 'world', 'foo': 'bar', 'with spaces': 'withoutspaces', 1: 2}
|
|
|
|
for k, v in data.items():
|
|
self.assertEqual(report_tags.getkey(data, k), v)
|
|
|
|
def test_asset(self):
|
|
"""Tests for asset files."""
|
|
# Test that an error is raised if the file does not exist
|
|
for b in [True, False]:
|
|
self.debug_mode(b)
|
|
|
|
with self.assertRaises(FileNotFoundError):
|
|
report_tags.asset('bad_file.txt')
|
|
|
|
# Create an asset file
|
|
asset_dir = settings.MEDIA_ROOT.joinpath('report', 'assets')
|
|
asset_dir.mkdir(parents=True, exist_ok=True)
|
|
asset_path = asset_dir.joinpath('test.txt')
|
|
|
|
asset_path.write_text('dummy data')
|
|
|
|
self.debug_mode(True)
|
|
asset = report_tags.asset('test.txt')
|
|
self.assertEqual(asset, '/media/report/assets/test.txt')
|
|
|
|
# Ensure that a 'safe string' also works
|
|
asset = report_tags.asset(SafeString('test.txt'))
|
|
self.assertEqual(asset, '/media/report/assets/test.txt')
|
|
|
|
self.debug_mode(False)
|
|
asset = report_tags.asset('test.txt')
|
|
self.assertEqual(asset, f'file://{asset_dir}/test.txt')
|
|
|
|
def test_uploaded_image(self):
|
|
"""Tests for retrieving uploaded images."""
|
|
# Test for a missing image
|
|
for b in [True, False]:
|
|
self.debug_mode(b)
|
|
|
|
with self.assertRaises(FileNotFoundError):
|
|
report_tags.uploaded_image(
|
|
'/part/something/test.png', replace_missing=False
|
|
)
|
|
|
|
img = str(report_tags.uploaded_image('/part/something/other.png'))
|
|
|
|
if b:
|
|
self.assertIn('blank_image.png', img)
|
|
else:
|
|
self.assertIn('data:image/png;charset=utf-8;base64,', img)
|
|
|
|
# Create a dummy image
|
|
img_path = 'part/images/'
|
|
img_path = settings.MEDIA_ROOT.joinpath(img_path)
|
|
img_file = img_path.joinpath('test.jpg')
|
|
|
|
img_path.mkdir(parents=True, exist_ok=True)
|
|
img_file.write_text('dummy data')
|
|
|
|
# Test in debug mode. Returns blank image as dummy file is not a valid image
|
|
self.debug_mode(True)
|
|
img = report_tags.uploaded_image('part/images/test.jpg')
|
|
self.assertEqual(img, '/static/img/blank_image.png')
|
|
|
|
# Now, let's create a proper image
|
|
img = Image.new('RGB', (128, 128), color='RED')
|
|
img.save(img_file)
|
|
|
|
# Try again
|
|
img = report_tags.uploaded_image('part/images/test.jpg')
|
|
self.assertEqual(img, '/media/part/images/test.jpg')
|
|
|
|
# Ensure that a 'safe string' also works
|
|
img = report_tags.uploaded_image(SafeString('part/images/test.jpg'))
|
|
self.assertEqual(img, '/media/part/images/test.jpg')
|
|
|
|
self.debug_mode(False)
|
|
img = report_tags.uploaded_image('part/images/test.jpg')
|
|
self.assertTrue(img.startswith('data:image/png;charset=utf-8;base64,'))
|
|
|
|
img = report_tags.uploaded_image(SafeString('part/images/test.jpg'))
|
|
self.assertTrue(img.startswith('data:image/png;charset=utf-8;base64,'))
|
|
|
|
def test_part_image(self):
|
|
"""Unit tests for the 'part_image' tag."""
|
|
with self.assertRaises(TypeError):
|
|
report_tags.part_image(None)
|
|
|
|
def test_company_image(self):
|
|
"""Unit tests for the 'company_image' tag."""
|
|
with self.assertRaises(TypeError):
|
|
report_tags.company_image(None)
|
|
|
|
def test_logo_image(self):
|
|
"""Unit tests for the 'logo_image' tag."""
|
|
# By default, should return the core InvenTree logo
|
|
for b in [True, False]:
|
|
self.debug_mode(b)
|
|
logo = report_tags.logo_image()
|
|
self.assertIn('inventree.png', logo)
|
|
|
|
def test_maths_tags(self):
|
|
"""Simple tests for mathematical operator tags."""
|
|
self.assertEqual(report_tags.add(1, 2), 3)
|
|
self.assertEqual(report_tags.subtract(10, 4.2), 5.8)
|
|
self.assertEqual(report_tags.multiply(2.3, 4), 9.2)
|
|
self.assertEqual(report_tags.divide(100, 5), 20)
|
|
|
|
def test_number_tags(self):
|
|
"""Simple tests for number formatting tags."""
|
|
fn = report_tags.format_number
|
|
|
|
self.assertEqual(fn(1234), '1234')
|
|
self.assertEqual(fn(1234.5678, decimal_places=2), '1234.57')
|
|
self.assertEqual(fn(1234.5678, decimal_places=3), '1234.568')
|
|
self.assertEqual(fn(-9999.5678, decimal_places=2, separator=','), '-9,999.57')
|
|
self.assertEqual(
|
|
fn(9988776655.4321, integer=True, separator=' '), '9 988 776 655'
|
|
)
|
|
|
|
@override_settings(TIME_ZONE='America/New_York')
|
|
def test_date_tags(self):
|
|
"""Test for date formatting tags.
|
|
|
|
- Source timezone is Australia/Sydney
|
|
- Server timezone is America/New York
|
|
"""
|
|
time = timezone.datetime(
|
|
year=2024,
|
|
month=3,
|
|
day=13,
|
|
hour=12,
|
|
minute=30,
|
|
second=0,
|
|
tzinfo=ZoneInfo('Australia/Sydney'),
|
|
)
|
|
|
|
# Format a set of tests: timezone, format, expected
|
|
tests = [
|
|
(None, None, '2024-03-12T21:30:00-04:00'),
|
|
(None, '%d-%m-%y', '12-03-24'),
|
|
('UTC', None, '2024-03-13T01:30:00+00:00'),
|
|
('UTC', '%d-%B-%Y', '13-March-2024'),
|
|
('Europe/Amsterdam', None, '2024-03-13T02:30:00+01:00'),
|
|
('Europe/Amsterdam', '%y-%m-%d %H:%M', '24-03-13 02:30'),
|
|
]
|
|
|
|
for tz, fmt, expected in tests:
|
|
result = report_tags.format_datetime(time, tz, fmt)
|
|
self.assertEqual(result, expected)
|
|
|
|
def test_icon(self):
|
|
"""Test the icon template tag."""
|
|
for icon in [None, '', 'not:the-correct-format', 'any-non-existent-icon']:
|
|
self.assertEqual(report_tags.icon(icon), '')
|
|
|
|
self.assertEqual(
|
|
report_tags.icon('ti:package:outline'),
|
|
f'<i class="icon " style="font-family: inventree-icon-font-ti">{chr(int("eaff", 16))}</i>',
|
|
)
|
|
self.assertEqual(
|
|
report_tags.icon(
|
|
'ti:package:outline', **{'class': 'my-custom-class my-seconds-class'}
|
|
),
|
|
f'<i class="icon my-custom-class my-seconds-class" style="font-family: inventree-icon-font-ti">{chr(int("eaff", 16))}</i>',
|
|
)
|
|
|
|
def test_include_icon_fonts(self):
|
|
"""Test the include_icon_fonts template tag."""
|
|
style = report_tags.include_icon_fonts()
|
|
|
|
self.assertIn('@font-face {', style)
|
|
self.assertIn("font-family: 'inventree-icon-font-ti';", style)
|
|
self.assertIn('tabler-icons/tabler-icons.ttf', style)
|
|
self.assertIn('.icon {', style)
|
|
|
|
|
|
class BarcodeTagTest(TestCase):
|
|
"""Unit tests for the barcode template tags."""
|
|
|
|
def test_barcode(self):
|
|
"""Test the barcode generation tag."""
|
|
barcode = barcode_tags.barcode('12345')
|
|
|
|
self.assertIsInstance(barcode, str)
|
|
self.assertTrue(barcode.startswith('data:image/png;'))
|
|
|
|
# Try with a different format
|
|
barcode = barcode_tags.barcode('99999', format='BMP')
|
|
self.assertIsInstance(barcode, str)
|
|
self.assertTrue(barcode.startswith('data:image/bmp;'))
|
|
|
|
# Test empty tag
|
|
with self.assertRaises(ValueError):
|
|
barcode_tags.barcode('')
|
|
|
|
def test_qrcode(self):
|
|
"""Test the qrcode generation tag."""
|
|
# Test with default settings
|
|
qrcode = barcode_tags.qrcode('hello world')
|
|
self.assertIsInstance(qrcode, str)
|
|
self.assertTrue(qrcode.startswith('data:image/png;'))
|
|
self.assertEqual(len(qrcode), 700)
|
|
|
|
# Generate a much larger qrcode
|
|
qrcode = barcode_tags.qrcode(
|
|
'hello_world', version=2, box_size=50, format='BMP'
|
|
)
|
|
self.assertIsInstance(qrcode, str)
|
|
self.assertTrue(qrcode.startswith('data:image/bmp;'))
|
|
self.assertEqual(len(qrcode), 309720)
|
|
|
|
# Test empty tag
|
|
with self.assertRaises(ValueError):
|
|
barcode_tags.qrcode('')
|
|
|
|
def test_datamatrix(self):
|
|
"""Test the datamatrix generation tag."""
|
|
# Test with default settings
|
|
datamatrix = barcode_tags.datamatrix('hello world')
|
|
self.assertEqual(
|
|
datamatrix,
|
|
'data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAIAAADZrBkAAAAAlElEQVR4nJ1TQQ7AIAgri///cncw6wroEseBgEFbCgZJnNsFICKOPAAIjeSM5T11IznK5f5WRMgnkhP9JfCcTC/MxFZ5hxLOgqrn3o/z/OqtsNpdSL31Iu9W4Dq8Sulu+q5Nuqa3XYOdnuidlICPpXhZVBruyzAKSZehT+yNlzvZQcq6JiW7Ni592swf/43kdlDfdgMk1eOtR7kWpAAAAABJRU5ErkJggg==',
|
|
)
|
|
|
|
datamatrix = barcode_tags.datamatrix(
|
|
'hello world', border=3, fill_color='red', back_color='blue'
|
|
)
|
|
self.assertEqual(
|
|
datamatrix,
|
|
'data:image/png;charset=utf-8;base64,iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAIAAABL1vtsAAAAqElEQVR4nN1UQQ6AMAgrxv9/GQ9mpJYSY/QkBxM3KLUUA0i8i+1l/dcQiXj09CwSEU2aQJ7nE8ou2faVUXoPZSEkq+dZKVxWg4UqxUHnVdkp6IdwMXMulGvzNBDMk4WwPSrUF3LNnQNZBJmOsZaVXa44QSEKnvWb5mIgKon1E1H6aPyOcIa15uhONP9aR4hSCiGmYAoYpj4uO+vK4+ybMhr8Nkjmn/z4Dvoldi8uJu4iAAAAAElFTkSuQmCC',
|
|
)
|
|
|
|
# Test empty tag
|
|
with self.assertRaises(ValueError):
|
|
barcode_tags.datamatrix('')
|
|
|
|
|
|
class ReportTest(InvenTreeAPITestCase):
|
|
"""Base class for unit testing reporting models."""
|
|
|
|
fixtures = [
|
|
'category',
|
|
'part',
|
|
'company',
|
|
'location',
|
|
'test_templates',
|
|
'supplier_part',
|
|
'stock',
|
|
'stock_tests',
|
|
'bom',
|
|
'build',
|
|
'order',
|
|
'return_order',
|
|
'sales_order',
|
|
]
|
|
|
|
superuser = True
|
|
|
|
def setUp(self):
|
|
"""Ensure cache is cleared as part of test setup."""
|
|
cache.clear()
|
|
|
|
apps.get_app_config('report').create_default_reports()
|
|
|
|
return super().setUp()
|
|
|
|
def test_list_endpoint(self):
|
|
"""Test that the LIST endpoint works for each report."""
|
|
url = reverse('api-report-template-list')
|
|
|
|
response = self.get(url)
|
|
self.assertEqual(response.status_code, 200)
|
|
|
|
reports = ReportTemplate.objects.all()
|
|
|
|
n = len(reports)
|
|
# API endpoint must return correct number of reports
|
|
self.assertEqual(len(response.data), n)
|
|
|
|
# Filter by "enabled" status
|
|
response = self.get(url, {'enabled': True})
|
|
self.assertEqual(len(response.data), n)
|
|
|
|
response = self.get(url, {'enabled': False})
|
|
self.assertEqual(len(response.data), 0)
|
|
|
|
# Disable each report
|
|
for report in reports:
|
|
report.enabled = False
|
|
report.save()
|
|
|
|
# Filter by "enabled" status
|
|
response = self.get(url, {'enabled': True})
|
|
self.assertEqual(len(response.data), 0)
|
|
|
|
response = self.get(url, {'enabled': False})
|
|
self.assertEqual(len(response.data), n)
|
|
|
|
# Filter by items
|
|
part_pk = Part.objects.first().pk
|
|
report = ReportTemplate.objects.filter(model_type='part').first()
|
|
return
|
|
# TODO @matmair re-enable this (in GitHub Actions) flaky test
|
|
response = self.get(url, {'model_type': 'part', 'items': part_pk})
|
|
self.assertEqual(len(response.data), 1)
|
|
self.assertEqual(response.data[0]['pk'], report.pk)
|
|
self.assertEqual(response.data[0]['name'], report.name)
|
|
|
|
def test_create_endpoint(self):
|
|
"""Test that creating a new report works for each report."""
|
|
url = reverse('api-report-template-list')
|
|
|
|
# Create a new report
|
|
# Django REST API "APITestCase" does not work like requests - to send a file without it existing on disk,
|
|
# create it as a StringIO object, and upload it under parameter template
|
|
filestr = StringIO(
|
|
'{% extends "label/report_base.html" %}{% block content %}<pre>TEST REPORT</pre>{% endblock content %}'
|
|
)
|
|
filestr.name = 'ExampleTemplate.html'
|
|
|
|
data = {
|
|
'name': 'New report',
|
|
'description': 'A fancy new report created through API test',
|
|
'template': filestr,
|
|
'model_type': 'part2',
|
|
}
|
|
|
|
# Test with invalid model type
|
|
response = self.post(url, data=data, expected_code=400)
|
|
|
|
self.assertIn('"part2" is not a valid choice', str(response.data['model_type']))
|
|
|
|
# With valid model type
|
|
data['model_type'] = 'part'
|
|
filestr.seek(0)
|
|
|
|
response = self.post(url, data=data, format=None, expected_code=201)
|
|
|
|
# Make sure the expected keys are in the response
|
|
self.assertIn('pk', response.data)
|
|
self.assertIn('name', response.data)
|
|
self.assertIn('description', response.data)
|
|
self.assertIn('template', response.data)
|
|
self.assertIn('filters', response.data)
|
|
self.assertIn('enabled', response.data)
|
|
|
|
self.assertEqual(response.data['name'], 'New report')
|
|
self.assertEqual(
|
|
response.data['description'], 'A fancy new report created through API test'
|
|
)
|
|
self.assertTrue(response.data['template'].endswith('ExampleTemplate.html'))
|
|
|
|
def test_detail_endpoint(self):
|
|
"""Test that the DETAIL endpoint works for each report."""
|
|
reports = ReportTemplate.objects.all()
|
|
|
|
n = len(reports)
|
|
|
|
# Make sure at least one report defined
|
|
self.assertGreaterEqual(n, 1)
|
|
|
|
# Check detail page for first report
|
|
response = self.get(
|
|
reverse('api-report-template-detail', kwargs={'pk': reports[0].pk}),
|
|
expected_code=200,
|
|
)
|
|
|
|
# Make sure the expected keys are in the response
|
|
self.assertIn('pk', response.data)
|
|
self.assertIn('name', response.data)
|
|
self.assertIn('description', response.data)
|
|
self.assertIn('template', response.data)
|
|
self.assertIn('filters', response.data)
|
|
self.assertIn('enabled', response.data)
|
|
|
|
filestr = StringIO(
|
|
'{% extends "label/report_base.html" %}{% block content %}<pre>TEST REPORT VERSION 2</pre>{% endblock content %}'
|
|
)
|
|
filestr.name = 'ExampleTemplate_Updated.html'
|
|
|
|
# Check PATCH method
|
|
response = self.patch(
|
|
reverse('api-report-template-detail', kwargs={'pk': reports[0].pk}),
|
|
{
|
|
'name': 'Changed name during test',
|
|
'description': 'New version of the template',
|
|
'template': filestr,
|
|
},
|
|
format=None,
|
|
expected_code=200,
|
|
)
|
|
|
|
# Make sure the expected keys are in the response
|
|
self.assertIn('pk', response.data)
|
|
self.assertIn('name', response.data)
|
|
self.assertIn('description', response.data)
|
|
self.assertIn('template', response.data)
|
|
self.assertIn('filters', response.data)
|
|
self.assertIn('enabled', response.data)
|
|
|
|
self.assertEqual(response.data['name'], 'Changed name during test')
|
|
self.assertEqual(response.data['description'], 'New version of the template')
|
|
|
|
self.assertTrue(
|
|
response.data['template'].endswith('ExampleTemplate_Updated.html')
|
|
)
|
|
|
|
# Delete the last report
|
|
response = self.delete(
|
|
reverse('api-report-template-detail', kwargs={'pk': reports[n - 1].pk}),
|
|
expected_code=204,
|
|
)
|
|
|
|
def test_metadata(self):
|
|
"""Unit tests for the metadata field."""
|
|
p = ReportTemplate.objects.first()
|
|
|
|
self.assertEqual(p.metadata, {})
|
|
|
|
self.assertIsNone(p.get_metadata('test'))
|
|
self.assertEqual(p.get_metadata('test', backup_value=123), 123)
|
|
|
|
# Test update via the set_metadata() method
|
|
p.set_metadata('test', 3)
|
|
self.assertEqual(p.get_metadata('test'), 3)
|
|
|
|
for k in ['apple', 'banana', 'carrot', 'carrot', 'banana']:
|
|
p.set_metadata(k, k)
|
|
|
|
self.assertEqual(len(p.metadata.keys()), 4)
|
|
|
|
def test_report_template_permissions(self):
|
|
"""Test that the user permissions are correctly applied.
|
|
|
|
- For all /api/report/ endpoints, any authenticated user should have full read access
|
|
- Write access is limited to staff users
|
|
- Non authenticated users should have no access at all
|
|
"""
|
|
# First test the "report list" endpoint
|
|
url = reverse('api-report-template-list')
|
|
|
|
template = ReportTemplate.objects.first()
|
|
|
|
detail_url = reverse('api-report-template-detail', kwargs={'pk': template.pk})
|
|
|
|
# Non-authenticated user should have no access
|
|
self.logout()
|
|
|
|
self.get(url, expected_code=401)
|
|
|
|
# Authenticated user should have read access
|
|
self.user.is_staff = False
|
|
self.user.save()
|
|
|
|
self.login()
|
|
|
|
# Check read access to template list URL
|
|
self.get(url, expected_code=200)
|
|
|
|
# Check read access to template detail URL
|
|
self.get(detail_url, expected_code=200)
|
|
|
|
# An update to the report template should fail
|
|
self.patch(
|
|
detail_url,
|
|
data={'description': 'Some new description here?'},
|
|
expected_code=403,
|
|
)
|
|
|
|
# Now, test with a staff user
|
|
self.logout()
|
|
|
|
self.user.is_staff = True
|
|
self.user.save()
|
|
|
|
self.login()
|
|
|
|
self.patch(
|
|
detail_url,
|
|
data={'description': 'An updated description'},
|
|
expected_code=200,
|
|
)
|
|
|
|
template.refresh_from_db()
|
|
self.assertEqual(template.description, 'An updated description')
|
|
|
|
|
|
class PrintTestMixins:
|
|
"""Mixin that enables e2e printing tests."""
|
|
|
|
plugin_ref = 'samplelabelprinter'
|
|
|
|
def do_activate_plugin(self):
|
|
"""Activate the 'samplelabel' plugin."""
|
|
plugin = registry.get_plugin(self.plugin_ref)
|
|
self.assertIsNotNone(plugin)
|
|
config = plugin.plugin_config()
|
|
self.assertIsNotNone(config)
|
|
config.active = True
|
|
config.save()
|
|
|
|
def run_print_test(self, qs, model_type, label: bool = True):
|
|
"""Run tests on single and multiple page printing.
|
|
|
|
Args:
|
|
qs: class of the base queryset
|
|
model_type: the model type of the queryset
|
|
label: whether the model is a label or report
|
|
"""
|
|
mdl = LabelTemplate if label else ReportTemplate
|
|
url = reverse('api-label-print' if label else 'api-report-print')
|
|
|
|
qs = qs.objects.all()
|
|
template = mdl.objects.filter(enabled=True, model_type=model_type).first()
|
|
plugin = registry.get_plugin(self.plugin_ref)
|
|
|
|
# Single page printing
|
|
self.post(
|
|
url,
|
|
{'template': template.pk, 'plugin': plugin.pk, 'items': [qs[0].pk]},
|
|
expected_code=201,
|
|
)
|
|
|
|
# Multi page printing
|
|
self.post(
|
|
url,
|
|
{
|
|
'template': template.pk,
|
|
'plugin': plugin.pk,
|
|
'items': [item.pk for item in qs],
|
|
},
|
|
expected_code=201,
|
|
max_query_time=15,
|
|
max_query_count=500 * len(qs),
|
|
)
|
|
|
|
# Test with wrong dimensions
|
|
if not hasattr(template, 'width'):
|
|
return
|
|
|
|
org_width = template.width
|
|
template.width = 0
|
|
template.save()
|
|
response = self.post(
|
|
url,
|
|
{'template': template.pk, 'plugin': plugin.pk, 'items': [qs[0].pk]},
|
|
expected_code=400,
|
|
)
|
|
self.assertEqual(str(response.data['template'][0]), 'Invalid label dimensions')
|
|
template.width = org_width
|
|
template.save()
|
|
|
|
|
|
class TestReportTest(PrintTestMixins, ReportTest):
|
|
"""Unit testing class for the stock item TestReport model."""
|
|
|
|
model = report_models.ReportTemplate
|
|
|
|
list_url = 'api-report-template-list'
|
|
detail_url = 'api-report-template-detail'
|
|
print_url = 'api-report-print'
|
|
|
|
def setUp(self):
|
|
"""Setup function for the stock item TestReport."""
|
|
apps.get_app_config('report').create_default_reports()
|
|
self.do_activate_plugin()
|
|
|
|
return super().setUp()
|
|
|
|
def test_print(self):
|
|
"""Printing tests for the TestReport."""
|
|
template = ReportTemplate.objects.filter(
|
|
enabled=True, model_type='stockitem'
|
|
).first()
|
|
|
|
self.assertIsNotNone(template)
|
|
|
|
# Ensure that the 'attach_to_model' attribute is initially False
|
|
template.attach_to_model = False
|
|
template.save()
|
|
template.refresh_from_db()
|
|
|
|
self.assertFalse(template.attach_to_model)
|
|
|
|
url = reverse(self.print_url)
|
|
|
|
# Try to print without providing a valid StockItem
|
|
self.post(url, {'template': template.pk}, expected_code=400)
|
|
|
|
# Try to print with an invalid StockItem
|
|
self.post(url, {'template': template.pk, 'items': [9999]}, expected_code=400)
|
|
|
|
# Now print with a valid StockItem
|
|
item = StockItem.objects.first()
|
|
|
|
n = item.attachments.count()
|
|
|
|
response = self.post(
|
|
url, {'template': template.pk, 'items': [item.pk]}, expected_code=201
|
|
)
|
|
|
|
# There should be a link to the generated PDF
|
|
self.assertTrue(response.data['output'].startswith('/media/report/'))
|
|
self.assertTrue(response.data['output'].endswith('.pdf'))
|
|
|
|
# By default, this should *not* have created an attachment against this stockitem
|
|
self.assertEqual(n, item.attachments.count())
|
|
self.assertFalse(
|
|
Attachment.objects.filter(model_id=item.pk, model_type='stockitem').exists()
|
|
)
|
|
|
|
# Now try again, but attach the generated PDF to the StockItem
|
|
template.attach_to_model = True
|
|
template.save()
|
|
|
|
response = self.post(
|
|
url, {'template': template.pk, 'items': [item.pk]}, expected_code=201
|
|
)
|
|
|
|
# A new attachment should have been created
|
|
self.assertEqual(n + 1, item.attachments.count())
|
|
attachment = item.attachments.order_by('-pk').first()
|
|
|
|
# The attachment should be a PDF
|
|
self.assertTrue(attachment.attachment.name.endswith('.pdf'))
|
|
|
|
def test_mdl_build(self):
|
|
"""Test the Build model."""
|
|
self.run_print_test(Build, 'build', label=False)
|
|
|
|
def test_mdl_returnorder(self):
|
|
"""Test the ReturnOrder model."""
|
|
self.run_print_test(ReturnOrder, 'returnorder', label=False)
|
|
|
|
def test_mdl_salesorder(self):
|
|
"""Test the SalesOrder model."""
|
|
self.run_print_test(SalesOrder, 'salesorder', label=False)
|
|
|
|
|
|
class AdminTest(AdminTestCase):
|
|
"""Tests for the admin interface integration."""
|
|
|
|
def test_admin(self):
|
|
"""Test the admin URL."""
|
|
self.helper(model=ReportTemplate)
|