django-bom 1.252__py3-none-any.whl → 1.257__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bom/tests.py CHANGED
@@ -1,5 +1,5 @@
1
1
  import csv
2
- from re import finditer, search
2
+ from re import finditer
3
3
  from unittest import skip
4
4
 
5
5
  from django.conf import settings
@@ -16,12 +16,11 @@ from .helpers import (
16
16
  create_a_fake_subpart,
17
17
  create_some_fake_manufacturers,
18
18
  create_some_fake_part_classes,
19
+ create_some_fake_part_revision_property_definitions,
19
20
  create_some_fake_parts,
20
- create_some_fake_sellers,
21
21
  create_user_and_organization,
22
22
  )
23
- from .models import ManufacturerPart, Part, PartClass, Seller, SellerPart, Subpart
24
-
23
+ from .models import Part, PartClass, Seller, Subpart
25
24
 
26
25
  TEST_FILES_DIR = "bom/test_files"
27
26
 
@@ -186,6 +185,7 @@ class TestBOM(TransactionTestCase):
186
185
  self.assertEqual(response.status_code, 200)
187
186
 
188
187
  def test_part_upload_bom(self):
188
+ sheen, voltage, _, _, _ = create_some_fake_part_revision_property_definitions(self.organization, False)
189
189
  (p1, p2, p3, p4) = create_some_fake_parts(organization=self.organization)
190
190
 
191
191
  test_file = 'test_bom.csv' if self.organization.number_variation_len > 0 else 'test_bom_6_no_variations.csv'
@@ -195,7 +195,7 @@ class TestBOM(TransactionTestCase):
195
195
 
196
196
  messages = list(response.context.get('messages'))
197
197
  for msg in messages:
198
- self.assertNotEqual(msg.tags, "error")
198
+ self.assertNotEqual(msg.tags, "error", msg.message)
199
199
 
200
200
  subparts = p2.latest().assembly.subparts.all()
201
201
 
@@ -227,7 +227,21 @@ class TestBOM(TransactionTestCase):
227
227
  bom = p1.latest().indented()
228
228
  self.assertEqual(len(bom.parts), 3)
229
229
 
230
+ def test_part_upload_bom_with_properties(self):
231
+ sheen, voltage, _, _, _ = create_some_fake_part_revision_property_definitions(self.organization)
232
+ (p1, p2, p3, p4) = create_some_fake_parts(organization=self.organization)
233
+
234
+ with open(f'{TEST_FILES_DIR}/test_bom_7_properties.csv') as test_csv:
235
+ response = self.client.post(reverse('bom:part-upload-bom', kwargs={'part_id': p2.id}), {'file': test_csv},
236
+ follow=True)
237
+ self.assertEqual(response.status_code, 200)
238
+
239
+ messages = list(response.context.get('messages'))
240
+ for msg in messages:
241
+ self.assertNotEqual(msg.tags, "error", msg.message)
242
+
230
243
  def test_upload_bom(self):
244
+ create_some_fake_part_revision_property_definitions(self.organization, some_required=False)
231
245
  (p1, p2, p3, p4) = create_some_fake_parts(organization=self.organization)
232
246
 
233
247
  # Test OK page visit
@@ -253,7 +267,8 @@ class TestBOM(TransactionTestCase):
253
267
  parent_part = Part.from_part_number(parent_part_number, organization=self.organization)
254
268
  bom = parent_part.indented()
255
269
  bom_list = list(bom.parts.values())
256
- self.assertEqual(len(bom.parts), len(test_list))
270
+ for idx, row in enumerate(test_list):
271
+ self.assertEqual(row['part_number'], bom_list[idx].part.full_part_number(), f'Row {idx + 1}')
257
272
 
258
273
  # Check that we successfully updated an existing part (only tested for semi-intelligent scheme for now)
259
274
  if self.organization.number_scheme == constants.NUMBER_SCHEME_SEMI_INTELLIGENT:
@@ -330,7 +345,17 @@ class TestBOM(TransactionTestCase):
330
345
  self.assertTrue("it would cause infinite recursion. Uploading of this subpart skipped." in str(msg.message))
331
346
  self.assertTrue("Row 15" in str(msg.message))
332
347
 
348
+ def test_upload_bom_with_properties(self):
349
+ (p1, p2, p3, p4) = create_some_fake_parts(organization=self.organization)
350
+
351
+ # Test OK upload
352
+ test_file = 'test_full_bom.csv'
353
+ with open(f'{TEST_FILES_DIR}/{test_file}') as test_csv:
354
+ response = self.client.post(reverse('bom:upload-bom'), {'file': test_csv}, follow=True)
355
+ self.assertEqual(response.status_code, 200)
356
+
333
357
  def test_part_upload_bom_corner_cases(self):
358
+ create_some_fake_part_revision_property_definitions(self.organization, False)
334
359
  (p1, p2, p3, p4) = create_some_fake_parts(organization=self.organization)
335
360
  with open(f'{TEST_FILES_DIR}/test_bom_3_recursion.csv') as test_csv:
336
361
  response = self.client.post(reverse('bom:part-upload-bom', kwargs={'part_id': p1.id}), {'file': test_csv}, follow=True)
@@ -347,7 +372,7 @@ class TestBOM(TransactionTestCase):
347
372
 
348
373
  messages = list(response.context.get('messages'))
349
374
  for msg in messages:
350
- self.assertNotEqual(msg.tags, "error") # Should be OK since we will default revision to 1
375
+ self.assertNotEqual(msg.tags, "error", msg.message) # Should be OK since we will default revision to 1
351
376
 
352
377
  def test_export_part_list(self):
353
378
  create_some_fake_parts(organization=self.organization)
@@ -371,14 +396,37 @@ class TestBOM(TransactionTestCase):
371
396
  self.assertEqual(part_classes.count(), 1)
372
397
  part_class = part_classes[0]
373
398
 
374
- # Test edit
399
+ # Test edit with property definitions
400
+ _, prop_def, _, _, _ = create_some_fake_part_revision_property_definitions(self.organization, False)
375
401
  part_class_form_data['name'] = 'edited test part name'
402
+ part_class_form_data.update({
403
+ 'prop-def-TOTAL_FORMS': '1',
404
+ 'prop-def-INITIAL_FORMS': '0',
405
+ 'prop-def-MIN_NUM_FORMS': '0',
406
+ 'prop-def-MAX_NUM_FORMS': '1000',
407
+ 'prop-def-0-property_definition': prop_def.id,
408
+ })
376
409
 
377
410
  response = self.client.post(reverse('bom:part-class-edit', kwargs={'part_class_id': part_class.id}), part_class_form_data)
378
411
  self.assertEqual(response.status_code, 302)
379
412
 
380
- part_class = PartClass.objects.get(id=part_class.id)
413
+ part_class.refresh_from_db()
381
414
  self.assertEqual(part_class.name, part_class_form_data['name'])
415
+ self.assertEqual(part_class.property_definitions.count(), 1)
416
+ prop_def = part_class.property_definitions.first()
417
+ self.assertEqual(prop_def.name, 'Voltage')
418
+
419
+ # Test deleting property definition
420
+ part_class_form_data.update({
421
+ 'prop-def-INITIAL_FORMS': '1',
422
+ 'prop-def-0-property_definition': prop_def.id,
423
+ 'prop-def-0-DELETE': 'on',
424
+ })
425
+ response = self.client.post(reverse('bom:part-class-edit', kwargs={'part_class_id': part_class.id}),
426
+ part_class_form_data)
427
+ self.assertEqual(response.status_code, 302)
428
+ part_class.refresh_from_db()
429
+ self.assertEqual(part_class.property_definitions.count(), 0)
382
430
 
383
431
  def test_create_part(self):
384
432
  (p1, p2, p3, p4) = create_some_fake_parts(organization=self.organization)
@@ -393,8 +441,8 @@ class TestBOM(TransactionTestCase):
393
441
  'configuration': 'W',
394
442
  'description': 'IC, MCU 32 Bit',
395
443
  'revision': 'A',
396
- 'attribute': '',
397
- 'value': ''
444
+ 'property_sheen': 'flat',
445
+ 'property_voltage': '1.34',
398
446
  }
399
447
 
400
448
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -417,6 +465,8 @@ class TestBOM(TransactionTestCase):
417
465
  'number_item': '9999',
418
466
  'description': 'IC, MCU 32 Bit',
419
467
  'revision': 'A',
468
+ 'property_sheen': 'flat',
469
+ 'property_voltage': '1.34',
420
470
  }
421
471
 
422
472
  if self.organization.number_variation_len > 0:
@@ -434,6 +484,8 @@ class TestBOM(TransactionTestCase):
434
484
  'number_variation': '',
435
485
  'description': 'IC, MCU 32 Bit',
436
486
  'revision': 'A',
487
+ 'property_sheen': 'flat',
488
+ 'property_voltage': '1.34',
437
489
  }
438
490
 
439
491
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -447,6 +499,8 @@ class TestBOM(TransactionTestCase):
447
499
  'number_item': '1234',
448
500
  'description': 'IC, MCU 32 Bit',
449
501
  'revision': 'A',
502
+ 'property_sheen': 'flat',
503
+ 'property_voltage': '1.34',
450
504
  }
451
505
 
452
506
  if self.organization.number_variation_len > 0:
@@ -464,6 +518,8 @@ class TestBOM(TransactionTestCase):
464
518
  'number_variation': '',
465
519
  'description': 'IC, MCU 32 Bit',
466
520
  'revision': 'A',
521
+ 'property_sheen': 'flat',
522
+ 'property_voltage': '1.34',
467
523
  }
468
524
 
469
525
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -479,6 +535,8 @@ class TestBOM(TransactionTestCase):
479
535
  'number_variation': '',
480
536
  'description': 'IC, MCU 32 Bit',
481
537
  'revision': 'A',
538
+ 'property_sheen': 'flat',
539
+ 'property_voltage': '1.34',
482
540
  }
483
541
 
484
542
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -506,8 +564,8 @@ class TestBOM(TransactionTestCase):
506
564
  'configuration': 'W',
507
565
  'description': 'IC, MCU 32 Bit',
508
566
  'revision': 'A',
509
- 'attribute': '',
510
- 'value': ''
567
+ 'property_sheen': 'flat',
568
+ 'property_voltage': '1.34',
511
569
  }
512
570
 
513
571
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -535,8 +593,8 @@ class TestBOM(TransactionTestCase):
535
593
  'configuration': 'W',
536
594
  'description': 'IC, MCU 32 Bit',
537
595
  'revision': 'A',
538
- 'attribute': '',
539
- 'value': ''
596
+ 'property_sheen': 'flat',
597
+ 'property_voltage': '1.34',
540
598
  }
541
599
 
542
600
  number_variation = None
@@ -709,17 +767,20 @@ class TestBOM(TransactionTestCase):
709
767
  found_error = True
710
768
  self.assertTrue(found_error)
711
769
 
712
- def test_upload_parts_break_tolerance(self):
713
- create_some_fake_part_classes(self.organization)
770
+ def test_upload_parts_break_too_many_characters(self):
771
+ pc1, _, _ = create_some_fake_part_classes(self.organization)
772
+ create_some_fake_part_revision_property_definitions(self.organization, part_class=pc1)
714
773
 
715
774
  # Should break with data error
716
775
  with open(f'{TEST_FILES_DIR}/test_new_parts_broken.csv') as test_csv:
717
776
  response = self.client.post(reverse('bom:upload-parts'), {'file': test_csv}, follow=True)
718
777
  messages = list(response.context.get('messages'))
719
778
 
720
- self.assertTrue(len(messages) > 0)
721
- for msg in messages:
722
- self.assertEqual(msg.tags, 'error')
779
+ self.assertTrue(len(messages) == 1)
780
+ msg = messages[0]
781
+ self.assertEqual(msg.tags, 'error', msg.message)
782
+ self.assertIn('Error on Row 2, property_sheen: Ensure this value has at most 255 characters (it has 483)',
783
+ msg.message)
723
784
 
724
785
  def test_upload_part_with_sellers(self):
725
786
  create_some_fake_part_classes(self.organization)
@@ -750,14 +811,14 @@ class TestBOM(TransactionTestCase):
750
811
  with open(f'{TEST_FILES_DIR}/test_part_classes_no_comment.csv') as test_csv:
751
812
  response = self.client.post(reverse('bom:settings'), {'file': test_csv, 'submit-part-class-upload': ''})
752
813
  self.assertEqual(response.status_code, 200)
753
- self.assertTrue('Part class 102 Resistor on row 3 is already defined. Uploading of this part class skipped.' in str(response.content))
814
+ self.assertTrue('Row 3: Part class 102 Resistor already defined.' in str(response.content))
754
815
 
755
816
  # Submit with a weird csv file that sort of works
756
817
  with open(f'{TEST_FILES_DIR}/test_part_classes_blank_rows.csv') as test_csv:
757
818
  response = self.client.post(reverse('bom:settings'), {'file': test_csv, 'submit-part-class-upload': ''})
758
819
  self.assertEqual(response.status_code, 200)
759
- self.assertTrue('in row 3 does not have a value. Uploading of this part class skipped.' in str(response.content))
760
- self.assertTrue('in row 4 does not have a value. Uploading of this part class skipped.' in str(response.content))
820
+ self.assertTrue('Row 3: Missing code.' in str(response.content))
821
+ self.assertTrue('Row 4: Missing code.' in str(response.content))
761
822
 
762
823
  # Submit with a csv file exported with a byte order mask, typically from MS word I think
763
824
  with open(f'{TEST_FILES_DIR}/test_part_classes_byte_order.csv') as test_csv:
@@ -1033,7 +1094,9 @@ class TestBOM(TransactionTestCase):
1033
1094
  'value': '10k',
1034
1095
  'part': p1.id,
1035
1096
  'configuration': 'W',
1036
- 'copy_assembly': 'False'
1097
+ 'copy_assembly': 'False',
1098
+ 'property_sheen': 'flat',
1099
+ 'property_voltage': '1.34',
1037
1100
  }
1038
1101
 
1039
1102
  response = self.client.post(
@@ -1048,7 +1111,9 @@ class TestBOM(TransactionTestCase):
1048
1111
  'revision': '5',
1049
1112
  'part': p3.id,
1050
1113
  'configuration': 'W',
1051
- 'copy_assembly': 'true'
1114
+ 'copy_assembly': 'true',
1115
+ 'property_sheen': 'flat',
1116
+ 'property_voltage': '1.34',
1052
1117
  }
1053
1118
 
1054
1119
  response = self.client.post(
@@ -1077,7 +1142,9 @@ class TestBOM(TransactionTestCase):
1077
1142
  'revision': '4',
1078
1143
  'attribute': 'resistance',
1079
1144
  'value': '10k',
1080
- 'part': p1.id
1145
+ 'part': p1.id,
1146
+ 'property_sheen': 'Flat',
1147
+ 'property_voltage': '0',
1081
1148
  }
1082
1149
 
1083
1150
  response = self.client.post(
@@ -1114,8 +1181,8 @@ class TestBOMIntelligent(TestBOM):
1114
1181
  'configuration': 'W',
1115
1182
  'description': 'IC, MCU 32 Bit',
1116
1183
  'revision': 'A',
1117
- 'attribute': '',
1118
- 'value': ''
1184
+ 'property_sheen': 'flat',
1185
+ 'property_voltage': '1.34',
1119
1186
  }
1120
1187
 
1121
1188
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -1137,6 +1204,8 @@ class TestBOMIntelligent(TestBOM):
1137
1204
  'number_item': '9999',
1138
1205
  'description': 'IC, MCU 32 Bit',
1139
1206
  'revision': 'A',
1207
+ 'property_sheen': 'flat',
1208
+ 'property_voltage': '1.34',
1140
1209
  }
1141
1210
 
1142
1211
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -1149,6 +1218,8 @@ class TestBOMIntelligent(TestBOM):
1149
1218
  'number_item': '5432',
1150
1219
  'description': 'IC, MCU 32 Bit',
1151
1220
  'revision': 'A',
1221
+ 'property_sheen': 'flat',
1222
+ 'property_voltage': '1.34',
1152
1223
  }
1153
1224
 
1154
1225
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -1161,6 +1232,8 @@ class TestBOMIntelligent(TestBOM):
1161
1232
  'number_item': '1234A',
1162
1233
  'description': 'IC, MCU 32 Bit',
1163
1234
  'revision': 'A',
1235
+ 'property_sheen': 'flat',
1236
+ 'property_voltage': '1.34',
1164
1237
  }
1165
1238
 
1166
1239
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -1173,6 +1246,8 @@ class TestBOMIntelligent(TestBOM):
1173
1246
  'number_item': '1235',
1174
1247
  'description': 'IC, MCU 32 Bit',
1175
1248
  'revision': 'A',
1249
+ 'property_sheen': 'flat',
1250
+ 'property_voltage': '1.34',
1176
1251
  }
1177
1252
 
1178
1253
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -1186,6 +1261,8 @@ class TestBOMIntelligent(TestBOM):
1186
1261
  'number_item': p1.number_item,
1187
1262
  'description': 'IC, MCU 32 Bit',
1188
1263
  'revision': 'A',
1264
+ 'property_sheen': 'flat',
1265
+ 'property_voltage': '1.34',
1189
1266
  }
1190
1267
 
1191
1268
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -1214,8 +1291,8 @@ class TestBOMIntelligent(TestBOM):
1214
1291
  'configuration': 'W',
1215
1292
  'description': 'IC, MCU 32 Bit',
1216
1293
  'revision': 'A',
1217
- 'attribute': '',
1218
- 'value': ''
1294
+ 'property_sheen': 'flat',
1295
+ 'property_voltage': '1.34',
1219
1296
  }
1220
1297
 
1221
1298
  response = self.client.post(reverse('bom:create-part'), new_part_form_data)
@@ -1252,7 +1329,7 @@ class TestBOMIntelligent(TestBOM):
1252
1329
 
1253
1330
  messages = list(response.context.get('messages'))
1254
1331
  for msg in messages:
1255
- self.assertNotEqual(msg.tags, "error") # Error loading 200-3333-00 via CSV because already in parent's BOM and has empty ref designators
1332
+ self.assertNotEqual(msg.tags, "error", msg.message)
1256
1333
 
1257
1334
  subparts = p2.latest().assembly.subparts.all()
1258
1335
 
@@ -1382,6 +1459,15 @@ class TestBOMNoVariation(TestBOM):
1382
1459
  def test_part_upload_bom_corner_cases(self):
1383
1460
  pass
1384
1461
 
1462
+ @skip('too specific of a test case for now...')
1463
+ def test_part_upload_bom_with_properties(self):
1464
+ pass
1465
+
1466
+ @skip('too specific of a test case for now...')
1467
+ def test_upload_parts_break_too_many_characters(self):
1468
+ pass
1469
+
1470
+
1385
1471
  @override_settings(BOM_CONFIG=settings.BOM_CONFIG_DEFAULT)
1386
1472
  class TestForms(TestCase):
1387
1473
  def setUp(self):
bom/urls.py CHANGED
@@ -31,6 +31,12 @@ bom_patterns = [
31
31
  path('export/', views.export_part_list, name='export-part-list'),
32
32
  path('user-meta/<int:user_meta_id>/edit/', views.user_meta_edit, name='user-meta-edit'),
33
33
  path('part-class/<int:part_class_id>/edit/', views.part_class_edit, name='part-class-edit'),
34
+ path('property-definition/add/', views.property_definition_edit, name='property-definition-add'),
35
+ path('property-definition/<int:property_definition_id>/edit/', views.property_definition_edit, name='property-definition-edit'),
36
+ path('property-definition/<int:property_definition_id>/delete/', views.property_definition_delete, name='property-definition-delete'),
37
+ path('quantity-of-measure/add/', views.quantity_of_measure_edit, name='quantity-of-measure-add'),
38
+ path('quantity-of-measure/<int:quantity_of_measure_id>/edit/', views.quantity_of_measure_edit, name='quantity-of-measure-edit'),
39
+ path('quantity-of-measure/<int:quantity_of_measure_id>/delete/', views.quantity_of_measure_delete, name='quantity-of-measure-delete'),
34
40
  path('create-part/', views.create_part, name='create-part'),
35
41
  path('upload-parts/', views.upload_parts, name='upload-parts'),
36
42
  path('upload-parts-help/', views.upload_parts_help, name='upload-parts-help'),