Coverage for src/backend/InvenTree/report/serializers.py: 88%

65 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-10-07 17:47 +0000

1"""API serializers for the reporting models.""" 

2 

3from django.utils.translation import gettext_lazy as _ 

4 

5from rest_framework import serializers 

6 

7import plugin.serializers 

8import report.helpers 

9import report.models 

10from InvenTree.serializers import ( 

11 InvenTreeAttachmentSerializerField, 

12 InvenTreeModelSerializer, 

13) 

14from users.serializers import UserSerializer 

15 

16 

17class ReportSerializerBase(InvenTreeModelSerializer): 

18 """Base serializer class for report and label templates.""" 

19 

20 def __init__(self, *args, **kwargs): 

21 """Override the constructor for the ReportSerializerBase. 

22 

23 The primary goal here is to ensure that the 'choices' attribute 

24 is set correctly for the 'model_type' field. 

25 """ 

26 super().__init__(*args, **kwargs) 

27 

28 if len(self.fields['model_type'].choices) == 0: 28 ↛ exitline 28 didn't return from function '__init__' because the condition on line 28 was always true

29 self.fields['model_type'].choices = report.helpers.report_model_options() 

30 

31 def save(self, **kwargs): 

32 """Override the save method to capture the user information.""" 

33 user = self.context.get('request').user 

34 

35 if not user or not user.is_authenticated: 35 ↛ 36line 35 didn't jump to line 36 because the condition on line 35 was never true

36 raise PermissionError( 

37 _('User must be authenticated to save report templates') 

38 ) 

39 

40 instance = super().save(**kwargs) 

41 instance.updated_by = user 

42 instance.save(increment_revision=False) 

43 

44 return instance 

45 

46 @staticmethod 

47 def base_fields(): 

48 """Base serializer field set.""" 

49 return [ 

50 'pk', 

51 'name', 

52 'description', 

53 'model_type', 

54 'template', 

55 'filters', 

56 'filename_pattern', 

57 'enabled', 

58 'revision', 

59 'attach_to_model', 

60 'updated', 

61 'updated_by', 

62 'updated_by_detail', 

63 ] 

64 

65 template = InvenTreeAttachmentSerializerField(required=True) 

66 

67 revision = serializers.IntegerField(read_only=True) 

68 

69 # Note: The choices are overridden at run-time 

70 model_type = serializers.ChoiceField( 

71 label=_('Model Type'), 

72 choices=report.helpers.report_model_options(), 

73 required=True, 

74 allow_blank=False, 

75 allow_null=False, 

76 ) 

77 

78 updated_by_detail = UserSerializer( 

79 source='updated_by', read_only=True, allow_null=True, many=False 

80 ) 

81 

82 

83class ReportTemplateSerializer(ReportSerializerBase): 

84 """Serializer class for report template model.""" 

85 

86 class Meta: 

87 """Metaclass options.""" 

88 

89 model = report.models.ReportTemplate 

90 fields = [ 

91 *ReportSerializerBase.base_fields(), 

92 'page_size', 

93 'landscape', 

94 'merge', 

95 ] 

96 

97 page_size = serializers.ChoiceField( 

98 required=False, 

99 default=report.helpers.report_page_size_default(), 

100 choices=report.helpers.report_page_size_options(), 

101 ) 

102 

103 

104class ReportPrintSerializer(serializers.Serializer): 

105 """Serializer class for printing a report.""" 

106 

107 class Meta: 

108 """Metaclass options.""" 

109 

110 fields = ['template', 'items'] 

111 

112 template = serializers.PrimaryKeyRelatedField( 

113 queryset=report.models.ReportTemplate.objects.filter(enabled=True), 

114 many=False, 

115 required=True, 

116 allow_null=False, 

117 label=_('Template'), 

118 help_text=_('Select report template'), 

119 ) 

120 

121 items = serializers.ListField( 

122 child=serializers.IntegerField(), 

123 required=True, 

124 allow_empty=False, 

125 label=_('Items'), 

126 help_text=_('List of item primary keys to include in the report'), 

127 ) 

128 

129 

130class LabelPrintSerializer(serializers.Serializer): 

131 """Serializer class for printing a label.""" 

132 

133 # List of extra plugin field names 

134 plugin_fields = [] 

135 

136 class Meta: 

137 """Metaclass options.""" 

138 

139 fields = ['template', 'items', 'plugin'] 

140 

141 def __init__(self, *args, **kwargs): 

142 """Override the constructor to add the extra plugin fields.""" 

143 # Reset to a known state 

144 self.Meta.fields = ['template', 'items', 'plugin'] 

145 

146 if plugin_serializer := kwargs.pop('plugin_serializer', None): 146 ↛ 147line 146 didn't jump to line 147 because the condition on line 146 was never true

147 for key, field in plugin_serializer.fields.items(): 

148 self.Meta.fields.append(key) 

149 setattr(self, key, field) 

150 

151 super().__init__(*args, **kwargs) 

152 

153 template = serializers.PrimaryKeyRelatedField( 

154 queryset=report.models.LabelTemplate.objects.filter(enabled=True), 

155 many=False, 

156 required=True, 

157 allow_null=False, 

158 label=_('Template'), 

159 help_text=_('Select label template'), 

160 ) 

161 

162 # Plugin field - note that we use the 'key' (not the pk) for lookup 

163 plugin = plugin.serializers.PluginRelationSerializer( 

164 many=False, 

165 required=False, 

166 allow_null=False, 

167 label=_('Printing Plugin'), 

168 help_text=_('Select plugin to use for label printing'), 

169 ) 

170 

171 items = serializers.ListField( 

172 child=serializers.IntegerField(), 

173 required=True, 

174 allow_empty=False, 

175 label=_('Items'), 

176 help_text=_('List of item primary keys to include in the report'), 

177 ) 

178 

179 

180class LabelTemplateSerializer(ReportSerializerBase): 

181 """Serializer class for label template model.""" 

182 

183 class Meta: 

184 """Metaclass options.""" 

185 

186 model = report.models.LabelTemplate 

187 fields = [*ReportSerializerBase.base_fields(), 'width', 'height'] 

188 

189 

190class ReportSnippetSerializer(InvenTreeModelSerializer): 

191 """Serializer class for the ReportSnippet model.""" 

192 

193 class Meta: 

194 """Metaclass options.""" 

195 

196 model = report.models.ReportSnippet 

197 

198 fields = ['pk', 'snippet', 'description'] 

199 

200 snippet = InvenTreeAttachmentSerializerField() 

201 

202 

203class ReportAssetSerializer(InvenTreeModelSerializer): 

204 """Serializer class for the ReportAsset model.""" 

205 

206 class Meta: 

207 """Meta class options.""" 

208 

209 model = report.models.ReportAsset 

210 fields = ['pk', 'asset', 'description'] 

211 

212 asset = InvenTreeAttachmentSerializerField()