django-bom 1.243__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/admin.py +60 -14
- bom/auth_backends.py +3 -1
- bom/constants.py +7 -0
- bom/csv_headers.py +22 -44
- bom/forms.py +971 -1043
- bom/helpers.py +79 -4
- bom/migrations/0051_alter_manufacturer_organization_and_more.py +41 -0
- bom/migrations/0052_remove_partrevision_attribute_and_more.py +576 -0
- bom/models.py +253 -99
- bom/settings.py +2 -0
- bom/static/bom/css/style.css +19 -0
- bom/static/bom/js/formset-handler.js +65 -0
- bom/templates/bom/edit-part-class.html +98 -11
- bom/templates/bom/edit-quantity-of-measure.html +119 -0
- bom/templates/bom/edit-user-meta.html +58 -26
- bom/templates/bom/organization-create.html +6 -3
- bom/templates/bom/part-info.html +10 -1
- bom/templates/bom/part-revision-display.html +22 -159
- bom/templates/bom/settings.html +142 -15
- bom/tests.py +117 -31
- bom/urls.py +6 -0
- bom/views/json_views.py +2 -2
- bom/views/views.py +194 -46
- {django_bom-1.243.dist-info → django_bom-1.257.dist-info}/METADATA +1 -1
- {django_bom-1.243.dist-info → django_bom-1.257.dist-info}/RECORD +28 -24
- {django_bom-1.243.dist-info → django_bom-1.257.dist-info}/WHEEL +0 -0
- {django_bom-1.243.dist-info → django_bom-1.257.dist-info}/licenses/LICENSE +0 -0
- {django_bom-1.243.dist-info → django_bom-1.257.dist-info}/top_level.txt +0 -0
bom/views/views.py
CHANGED
|
@@ -47,10 +47,14 @@ from bom.forms import (
|
|
|
47
47
|
PartInfoForm,
|
|
48
48
|
PartRevisionForm,
|
|
49
49
|
PartRevisionNewForm,
|
|
50
|
+
PartRevisionPropertyDefinitionForm,
|
|
51
|
+
PartRevisionPropertyDefinitionFormSet,
|
|
52
|
+
QuantityOfMeasureForm,
|
|
50
53
|
Seller,
|
|
51
54
|
SellerForm,
|
|
52
55
|
SellerPartForm,
|
|
53
56
|
SubpartForm,
|
|
57
|
+
UnitDefinitionFormSet,
|
|
54
58
|
UploadBOMForm,
|
|
55
59
|
UserAddForm,
|
|
56
60
|
UserCreateForm,
|
|
@@ -66,14 +70,18 @@ from bom.models import (
|
|
|
66
70
|
Part,
|
|
67
71
|
PartClass,
|
|
68
72
|
PartRevision,
|
|
73
|
+
PartRevisionPropertyDefinition,
|
|
74
|
+
QuantityOfMeasure,
|
|
69
75
|
SellerPart,
|
|
70
76
|
Subpart,
|
|
77
|
+
UnitDefinition,
|
|
71
78
|
User,
|
|
72
|
-
|
|
79
|
+
get_user_meta_model
|
|
73
80
|
)
|
|
74
81
|
from bom.utils import check_references_for_duplicates, listify_string, prep_for_sorting_nicely
|
|
75
82
|
|
|
76
83
|
logger = logging.getLogger(__name__)
|
|
84
|
+
UserMeta = get_user_meta_model()
|
|
77
85
|
BOM_LOGIN_URL = getattr(settings, "BOM_LOGIN_URL", None) or settings.LOGIN_URL
|
|
78
86
|
|
|
79
87
|
def form_error_messages(form_errors) -> [str]:
|
|
@@ -244,7 +252,7 @@ def home(request):
|
|
|
244
252
|
for field_name in csv_headers.get_default_all():
|
|
245
253
|
if field_name not in csv_headers.get_defaults_list(['part_number', 'part_category', 'part_synopsis', 'part_revision', 'part_manufacturer', 'part_manufacturer_part_number', ]
|
|
246
254
|
+ seller_csv_headers.get_default_all()):
|
|
247
|
-
attr =
|
|
255
|
+
attr = part_rev.get_field_value(field_name)
|
|
248
256
|
row.update({csv_headers.get_default(field_name): attr if attr is not None else ''})
|
|
249
257
|
else:
|
|
250
258
|
row = {
|
|
@@ -257,7 +265,7 @@ def home(request):
|
|
|
257
265
|
for field_name in csv_headers.get_default_all():
|
|
258
266
|
if field_name not in csv_headers.get_defaults_list(['part_number', 'part_synopsis', 'part_revision', 'part_manufacturer', 'part_manufacturer_part_number', ]
|
|
259
267
|
+ seller_csv_headers.get_default_all()):
|
|
260
|
-
attr =
|
|
268
|
+
attr = part_rev.get_field_value(field_name)
|
|
261
269
|
row.update({csv_headers.get_default(field_name): attr if attr is not None else ''})
|
|
262
270
|
|
|
263
271
|
sellerparts = part_rev.part.seller_parts()
|
|
@@ -355,6 +363,9 @@ def bom_settings(request, tab_anchor=None):
|
|
|
355
363
|
name = 'settings'
|
|
356
364
|
|
|
357
365
|
part_classes = PartClass.objects.all().filter(organization=organization)
|
|
366
|
+
property_definitions = PartRevisionPropertyDefinition.objects.available_to(organization=organization).order_by(
|
|
367
|
+
'name')
|
|
368
|
+
quantities_of_measure = QuantityOfMeasure.objects.available_to(organization=organization).order_by('name')
|
|
358
369
|
|
|
359
370
|
users_in_organization = User.objects.filter(
|
|
360
371
|
id__in=UserMeta.objects.filter(organization=organization).values_list('user', flat=True)).order_by(
|
|
@@ -394,14 +405,22 @@ def bom_settings(request, tab_anchor=None):
|
|
|
394
405
|
messages.error(request, "You need a Pro subscription to add users.")
|
|
395
406
|
elif not user_can_manage_members:
|
|
396
407
|
messages.error(request, "You are not allowed to manage users, contact your organization admin.")
|
|
408
|
+
elif not has_member_capacity:
|
|
409
|
+
messages.error(request,
|
|
410
|
+
"You have reached your organization's member capacity. Manage subscription to add more members.")
|
|
397
411
|
else:
|
|
398
412
|
user_add_form = UserAddForm(request.POST, organization=organization)
|
|
399
413
|
if user_add_form.is_valid():
|
|
400
414
|
added_user_profile = user_add_form.save()
|
|
401
415
|
messages.info(request, f"Added {added_user_profile.user.first_name} {added_user_profile.user.last_name} to your organization.")
|
|
402
416
|
else:
|
|
403
|
-
|
|
404
|
-
|
|
417
|
+
for field, errors in user_add_form.errors.items():
|
|
418
|
+
for error in errors:
|
|
419
|
+
messages.error(request, f"{field.capitalize()}: {error}")
|
|
420
|
+
users_in_organization.all()
|
|
421
|
+
users_in_organization_count = users_in_organization.count()
|
|
422
|
+
has_member_capacity = users_in_organization_count < organization.subscription_quantity
|
|
423
|
+
seats_available = max(organization.subscription_quantity - users_in_organization_count, 0)
|
|
405
424
|
elif 'clear-add-user' in request.POST:
|
|
406
425
|
tab_anchor = ORGANIZATION_TAB
|
|
407
426
|
user_add_form = UserAddForm()
|
|
@@ -421,7 +440,10 @@ def bom_settings(request, tab_anchor=None):
|
|
|
421
440
|
user_meta.save()
|
|
422
441
|
except UserMeta.DoesNotExist:
|
|
423
442
|
messages.error(request, "No user found with given id {}.".format(user_meta_id))
|
|
424
|
-
|
|
443
|
+
users_in_organization.all()
|
|
444
|
+
users_in_organization_count = users_in_organization.count()
|
|
445
|
+
has_member_capacity = users_in_organization_count < organization.subscription_quantity
|
|
446
|
+
seats_available = max(organization.subscription_quantity - users_in_organization_count, 0)
|
|
425
447
|
elif 'submit-edit-organization' in request.POST:
|
|
426
448
|
tab_anchor = ORGANIZATION_TAB
|
|
427
449
|
organization_form = OrganizationFormEditSettings(request.POST, instance=organization, user=user)
|
|
@@ -521,6 +543,8 @@ def bom_settings(request, tab_anchor=None):
|
|
|
521
543
|
profile.save()
|
|
522
544
|
if users_in_organization == 0:
|
|
523
545
|
organization.delete()
|
|
546
|
+
else:
|
|
547
|
+
messages.warning(request, "No action was taken because no form field was submitted.")
|
|
524
548
|
|
|
525
549
|
user_form = UserForm(instance=user)
|
|
526
550
|
user_add_form = UserAddForm()
|
|
@@ -697,29 +721,19 @@ def seller_delete(request, seller_id):
|
|
|
697
721
|
|
|
698
722
|
@login_required(login_url=BOM_LOGIN_URL)
|
|
699
723
|
def user_meta_edit(request, user_meta_id):
|
|
700
|
-
user = request.user
|
|
701
|
-
profile = user.bom_profile()
|
|
702
|
-
organization = profile.organization
|
|
703
|
-
|
|
704
724
|
user_meta = get_object_or_404(UserMeta, pk=user_meta_id)
|
|
705
|
-
|
|
706
|
-
|
|
725
|
+
user = user_meta.user
|
|
726
|
+
organization = user_meta.organization
|
|
727
|
+
title = f'Manage Member'
|
|
707
728
|
|
|
708
729
|
if request.method == 'POST':
|
|
709
|
-
|
|
710
|
-
if
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
user_meta_user_form.save()
|
|
714
|
-
user_meta_form.save()
|
|
715
|
-
return HttpResponseRedirect(reverse('bom:settings', kwargs={'tab_anchor': 'organization'}))
|
|
716
|
-
|
|
730
|
+
form = UserAddForm(request.POST, instance=user_meta, organization=organization, exclude_username=True)
|
|
731
|
+
if form.is_valid():
|
|
732
|
+
form.save()
|
|
733
|
+
return HttpResponseRedirect(reverse('bom:settings', kwargs={'tab_anchor': 'organization'}))
|
|
717
734
|
return TemplateResponse(request, 'bom/edit-user-meta.html', locals())
|
|
718
|
-
|
|
719
735
|
else:
|
|
720
|
-
|
|
721
|
-
user_meta_form = UserMetaForm(instance=user_meta, organization=organization)
|
|
722
|
-
|
|
736
|
+
form = UserAddForm(instance=user_meta, organization=organization, exclude_username=True)
|
|
723
737
|
return TemplateResponse(request, 'bom/edit-user-meta.html', locals())
|
|
724
738
|
|
|
725
739
|
|
|
@@ -940,7 +954,7 @@ def upload_bom(request):
|
|
|
940
954
|
else:
|
|
941
955
|
messages.error(request, upload_bom_form.errors)
|
|
942
956
|
else:
|
|
943
|
-
upload_bom_form = UploadBOMForm(initial={'organization': organization})
|
|
957
|
+
upload_bom_form = UploadBOMForm(organization=organization, initial={'organization': organization})
|
|
944
958
|
bom_csv_form = BOMCSVForm()
|
|
945
959
|
|
|
946
960
|
return TemplateResponse(request, 'bom/upload-bom.html', locals())
|
|
@@ -1019,22 +1033,15 @@ def export_part_list(request):
|
|
|
1019
1033
|
'number_item',
|
|
1020
1034
|
'number_variation')
|
|
1021
1035
|
|
|
1022
|
-
fieldnames = [
|
|
1023
|
-
'part_number',
|
|
1024
|
-
'part_synopsis',
|
|
1025
|
-
'part_revision',
|
|
1026
|
-
'part_manufacturer',
|
|
1027
|
-
'part_manufacturer_part_number',
|
|
1028
|
-
]
|
|
1029
|
-
|
|
1030
1036
|
csv_headers = organization.part_list_csv_headers()
|
|
1031
|
-
writer = csv.DictWriter(response, fieldnames=
|
|
1037
|
+
writer = csv.DictWriter(response, fieldnames=csv_headers.get_default_all())
|
|
1032
1038
|
writer.writeheader()
|
|
1033
1039
|
for item in parts:
|
|
1034
1040
|
try:
|
|
1041
|
+
latest_rev = item.latest()
|
|
1035
1042
|
row = {
|
|
1036
1043
|
csv_headers.get_default('part_number'): item.full_part_number(),
|
|
1037
|
-
csv_headers.get_default('part_revision'):
|
|
1044
|
+
csv_headers.get_default('part_revision'): latest_rev.revision if latest_rev else '',
|
|
1038
1045
|
csv_headers.get_default('part_manufacturer'): item.primary_manufacturer_part.manufacturer.name if item.primary_manufacturer_part is not None and item.primary_manufacturer_part.manufacturer is not None else '',
|
|
1039
1046
|
csv_headers.get_default('part_manufacturer_part_number'): item.primary_manufacturer_part.manufacturer_part_number if item.primary_manufacturer_part is not None and item.primary_manufacturer_part.manufacturer is not None else '',
|
|
1040
1047
|
}
|
|
@@ -1042,7 +1049,7 @@ def export_part_list(request):
|
|
|
1042
1049
|
if field_name not in csv_headers.get_defaults_list(
|
|
1043
1050
|
['part_number', 'part_category', 'part_synopsis', 'part_revision', 'part_manufacturer',
|
|
1044
1051
|
'part_manufacturer_part_number', ]):
|
|
1045
|
-
attr =
|
|
1052
|
+
attr = latest_rev.get_field_value(field_name) if latest_rev else None
|
|
1046
1053
|
row.update({csv_headers.get_default(field_name): attr if attr is not None else ''})
|
|
1047
1054
|
writer.writerow({k: smart_str(v) for k, v in row.items()})
|
|
1048
1055
|
|
|
@@ -1071,7 +1078,7 @@ def create_part(request):
|
|
|
1071
1078
|
part_form = PartForm(request.POST, organization=organization)
|
|
1072
1079
|
manufacturer_form = ManufacturerForm(request.POST)
|
|
1073
1080
|
manufacturer_part_form = ManufacturerPartForm(request.POST, organization=organization)
|
|
1074
|
-
part_revision_form = PartRevisionForm(request.POST)
|
|
1081
|
+
part_revision_form = PartRevisionForm(request.POST, organization=organization)
|
|
1075
1082
|
# Checking if part form is valid checks for number uniqueness
|
|
1076
1083
|
if part_form.is_valid() and manufacturer_form.is_valid() and manufacturer_part_form.is_valid():
|
|
1077
1084
|
mpn = manufacturer_part_form.cleaned_data['manufacturer_part_number']
|
|
@@ -1096,6 +1103,8 @@ def create_part(request):
|
|
|
1096
1103
|
new_part.number_class = None
|
|
1097
1104
|
new_part.number_variation = None
|
|
1098
1105
|
|
|
1106
|
+
part_revision_form = PartRevisionForm(request.POST, part_class=new_part.number_class,
|
|
1107
|
+
organization=organization)
|
|
1099
1108
|
if part_revision_form.is_valid():
|
|
1100
1109
|
# Save the Part before the PartRevision, as this will again check for part
|
|
1101
1110
|
# number uniqueness. This way if someone else(s) working concurrently is also
|
|
@@ -1106,7 +1115,8 @@ def create_part(request):
|
|
|
1106
1115
|
pr.part = new_part # Associate PartRevision with Part
|
|
1107
1116
|
pr.save()
|
|
1108
1117
|
except IntegrityError as err:
|
|
1109
|
-
messages.error(request, "Error! Already created a part with part number {0}-{1}-{
|
|
1118
|
+
messages.error(request, "Error! Already created a part with part number {0}-{1}-{2}}".format(
|
|
1119
|
+
new_part.number_class.code, new_part.number_item, new_part.number_variation))
|
|
1110
1120
|
return TemplateResponse(request, 'bom/create-part.html', locals())
|
|
1111
1121
|
else:
|
|
1112
1122
|
messages.error(request, part_revision_form.errors)
|
|
@@ -1126,7 +1136,8 @@ def create_part(request):
|
|
|
1126
1136
|
else:
|
|
1127
1137
|
# Initialize organization in the form's model and in the form itself:
|
|
1128
1138
|
part_form = PartForm(initial={'organization': organization}, organization=organization)
|
|
1129
|
-
part_revision_form = PartRevisionForm(initial={'revision': 1, 'organization': organization}
|
|
1139
|
+
part_revision_form = PartRevisionForm(initial={'revision': 1, 'organization': organization},
|
|
1140
|
+
organization=organization)
|
|
1130
1141
|
manufacturer_form = ManufacturerForm(initial={'organization': organization})
|
|
1131
1142
|
manufacturer_part_form = ManufacturerPartForm(organization=organization)
|
|
1132
1143
|
|
|
@@ -1266,6 +1277,117 @@ def remove_subpart(request, part_id, part_revision_id, subpart_id):
|
|
|
1266
1277
|
reverse('bom:part-manage-bom', kwargs={'part_id': part_id, 'part_revision_id': part_revision_id}))
|
|
1267
1278
|
|
|
1268
1279
|
|
|
1280
|
+
@login_required(login_url=BOM_LOGIN_URL)
|
|
1281
|
+
def property_definition_edit(request, property_definition_id=None):
|
|
1282
|
+
user = request.user
|
|
1283
|
+
profile = user.bom_profile()
|
|
1284
|
+
organization = profile.organization
|
|
1285
|
+
|
|
1286
|
+
if property_definition_id:
|
|
1287
|
+
property_definition = get_object_or_404(PartRevisionPropertyDefinition, pk=property_definition_id)
|
|
1288
|
+
if property_definition.organization != organization:
|
|
1289
|
+
messages.error(request, "Can't access a property definition that is not yours!")
|
|
1290
|
+
return HttpResponseRedirect(reverse('bom:settings', kwargs={'tab_anchor': 'indabom'}))
|
|
1291
|
+
title = f'Edit Property Definition {property_definition.name}'
|
|
1292
|
+
else:
|
|
1293
|
+
property_definition = None
|
|
1294
|
+
title = 'Add Property Definition'
|
|
1295
|
+
|
|
1296
|
+
if request.method == 'POST':
|
|
1297
|
+
form = PartRevisionPropertyDefinitionForm(request.POST, instance=property_definition, organization=organization)
|
|
1298
|
+
if form.is_valid():
|
|
1299
|
+
property_definition = form.save(commit=False)
|
|
1300
|
+
property_definition.organization = organization
|
|
1301
|
+
property_definition.save()
|
|
1302
|
+
return HttpResponseRedirect(reverse('bom:settings', kwargs={'tab_anchor': 'indabom'}))
|
|
1303
|
+
else:
|
|
1304
|
+
form = PartRevisionPropertyDefinitionForm(instance=property_definition, organization=organization)
|
|
1305
|
+
|
|
1306
|
+
return TemplateResponse(request, 'bom/bom-form.html', locals())
|
|
1307
|
+
|
|
1308
|
+
|
|
1309
|
+
@login_required(login_url=BOM_LOGIN_URL)
|
|
1310
|
+
@organization_admin
|
|
1311
|
+
def property_definition_delete(request, property_definition_id):
|
|
1312
|
+
user = request.user
|
|
1313
|
+
profile = user.bom_profile()
|
|
1314
|
+
organization = profile.organization
|
|
1315
|
+
property_definition = get_object_or_404(PartRevisionPropertyDefinition, pk=property_definition_id)
|
|
1316
|
+
if property_definition.organization != organization:
|
|
1317
|
+
messages.error(request, "Can't delete a property definition that is not yours!")
|
|
1318
|
+
else:
|
|
1319
|
+
property_definition.delete()
|
|
1320
|
+
return HttpResponseRedirect(reverse('bom:settings', kwargs={'tab_anchor': 'indabom'}))
|
|
1321
|
+
|
|
1322
|
+
|
|
1323
|
+
@login_required(login_url=BOM_LOGIN_URL)
|
|
1324
|
+
def quantity_of_measure_edit(request, quantity_of_measure_id=None):
|
|
1325
|
+
user = request.user
|
|
1326
|
+
profile = user.bom_profile()
|
|
1327
|
+
organization = profile.organization
|
|
1328
|
+
|
|
1329
|
+
if quantity_of_measure_id:
|
|
1330
|
+
quantity_of_measure = get_object_or_404(QuantityOfMeasure, pk=quantity_of_measure_id)
|
|
1331
|
+
if quantity_of_measure.organization != organization:
|
|
1332
|
+
messages.error(request, "Can't access a quantity of measure that is not yours!")
|
|
1333
|
+
return HttpResponseRedirect(reverse('bom:settings', kwargs={'tab_anchor': 'indabom'}))
|
|
1334
|
+
title = f'Edit Quantity of Measure {quantity_of_measure.name}'
|
|
1335
|
+
else:
|
|
1336
|
+
quantity_of_measure = None
|
|
1337
|
+
title = 'Add Quantity of Measure'
|
|
1338
|
+
|
|
1339
|
+
if request.method == 'POST':
|
|
1340
|
+
form = QuantityOfMeasureForm(request.POST, instance=quantity_of_measure, organization=organization)
|
|
1341
|
+
unit_formset = UnitDefinitionFormSet(
|
|
1342
|
+
request.POST,
|
|
1343
|
+
queryset=quantity_of_measure.units.all() if quantity_of_measure else UnitDefinition.objects.none(),
|
|
1344
|
+
form_kwargs={'organization': organization},
|
|
1345
|
+
prefix='units'
|
|
1346
|
+
)
|
|
1347
|
+
|
|
1348
|
+
if form.is_valid() and unit_formset.is_valid():
|
|
1349
|
+
quantity_of_measure = form.save()
|
|
1350
|
+
units = unit_formset.save(commit=False)
|
|
1351
|
+
base_multipliers = [float(unit.base_multiplier) for unit in units]
|
|
1352
|
+
if 1.0 not in base_multipliers:
|
|
1353
|
+
messages.error(request, "Must have a base multiplier of 1.0 for at least 1 unit.")
|
|
1354
|
+
return TemplateResponse(request, 'bom/edit-quantity-of-measure.html', locals())
|
|
1355
|
+
|
|
1356
|
+
for unit in units:
|
|
1357
|
+
unit.organization = organization
|
|
1358
|
+
unit.quantity_of_measure = quantity_of_measure
|
|
1359
|
+
unit.save()
|
|
1360
|
+
for obj in unit_formset.deleted_objects:
|
|
1361
|
+
obj.delete()
|
|
1362
|
+
return HttpResponseRedirect(reverse('bom:settings', kwargs={'tab_anchor': 'indabom'}))
|
|
1363
|
+
else:
|
|
1364
|
+
messages.error(request, form.errors)
|
|
1365
|
+
messages.error(request, unit_formset.errors)
|
|
1366
|
+
else:
|
|
1367
|
+
form = QuantityOfMeasureForm(instance=quantity_of_measure, organization=organization)
|
|
1368
|
+
unit_formset = UnitDefinitionFormSet(
|
|
1369
|
+
queryset=quantity_of_measure.units.all() if quantity_of_measure else UnitDefinition.objects.none(),
|
|
1370
|
+
form_kwargs={'organization': organization},
|
|
1371
|
+
prefix='units'
|
|
1372
|
+
)
|
|
1373
|
+
|
|
1374
|
+
return TemplateResponse(request, 'bom/edit-quantity-of-measure.html', locals())
|
|
1375
|
+
|
|
1376
|
+
|
|
1377
|
+
@login_required(login_url=BOM_LOGIN_URL)
|
|
1378
|
+
@organization_admin
|
|
1379
|
+
def quantity_of_measure_delete(request, quantity_of_measure_id):
|
|
1380
|
+
user = request.user
|
|
1381
|
+
profile = user.bom_profile()
|
|
1382
|
+
organization = profile.organization
|
|
1383
|
+
quantity_of_measure = get_object_or_404(QuantityOfMeasure, pk=quantity_of_measure_id)
|
|
1384
|
+
if quantity_of_measure.organization != organization:
|
|
1385
|
+
messages.error(request, "Can't delete a quantity of measure that is not yours!")
|
|
1386
|
+
else:
|
|
1387
|
+
quantity_of_measure.delete()
|
|
1388
|
+
return HttpResponseRedirect(reverse('bom:settings', kwargs={'tab_anchor': 'indabom'}))
|
|
1389
|
+
|
|
1390
|
+
|
|
1269
1391
|
@login_required(login_url=BOM_LOGIN_URL)
|
|
1270
1392
|
def part_class_edit(request, part_class_id):
|
|
1271
1393
|
user = request.user
|
|
@@ -1277,15 +1399,40 @@ def part_class_edit(request, part_class_id):
|
|
|
1277
1399
|
|
|
1278
1400
|
if request.method == 'POST':
|
|
1279
1401
|
part_class_form = PartClassForm(request.POST, instance=part_class, organization=organization)
|
|
1280
|
-
|
|
1281
|
-
|
|
1402
|
+
property_definitions_formset = PartRevisionPropertyDefinitionFormSet(
|
|
1403
|
+
request.POST,
|
|
1404
|
+
form_kwargs={'organization': organization},
|
|
1405
|
+
prefix='prop-def'
|
|
1406
|
+
)
|
|
1407
|
+
|
|
1408
|
+
if part_class_form.is_valid() and property_definitions_formset.is_valid():
|
|
1409
|
+
part_class = part_class_form.save()
|
|
1410
|
+
|
|
1411
|
+
# Clear current associations and re-add from formset
|
|
1412
|
+
part_class.property_definitions.clear()
|
|
1413
|
+
for form in property_definitions_formset:
|
|
1414
|
+
if form.cleaned_data and not form.cleaned_data.get('DELETE'):
|
|
1415
|
+
definition = form.cleaned_data.get('property_definition')
|
|
1416
|
+
if definition:
|
|
1417
|
+
part_class.property_definitions.add(definition)
|
|
1418
|
+
|
|
1282
1419
|
return HttpResponseRedirect(reverse('bom:settings', kwargs={'tab_anchor': 'indabom'}))
|
|
1283
1420
|
|
|
1284
1421
|
else:
|
|
1422
|
+
if not part_class_form.is_valid():
|
|
1423
|
+
messages.error(request, part_class_form.errors)
|
|
1424
|
+
if not property_definitions_formset.is_valid():
|
|
1425
|
+
messages.error(request, property_definitions_formset.errors)
|
|
1285
1426
|
return TemplateResponse(request, 'bom/edit-part-class.html', locals())
|
|
1286
1427
|
|
|
1287
1428
|
else:
|
|
1288
1429
|
part_class_form = PartClassForm(instance=part_class, organization=organization)
|
|
1430
|
+
initial = [{'property_definition': pd} for pd in part_class.property_definitions.all().order_by('name')]
|
|
1431
|
+
property_definitions_formset = PartRevisionPropertyDefinitionFormSet(
|
|
1432
|
+
initial=initial,
|
|
1433
|
+
form_kwargs={'organization': organization},
|
|
1434
|
+
prefix='prop-def',
|
|
1435
|
+
)
|
|
1289
1436
|
|
|
1290
1437
|
return TemplateResponse(request, 'bom/edit-part-class.html', locals())
|
|
1291
1438
|
|
|
@@ -1536,7 +1683,8 @@ def part_revision_new(request, part_id):
|
|
|
1536
1683
|
used_part_revisions = all_used_in_prs.filter(configuration='W')
|
|
1537
1684
|
|
|
1538
1685
|
if request.method == 'POST':
|
|
1539
|
-
part_revision_new_form = PartRevisionNewForm(request.POST, part=part, revision=next_revision_number,
|
|
1686
|
+
part_revision_new_form = PartRevisionNewForm(request.POST, part=part, revision=next_revision_number,
|
|
1687
|
+
assembly=latest_revision.assembly, organization=organization)
|
|
1540
1688
|
if part_revision_new_form.is_valid():
|
|
1541
1689
|
new_part_revision = part_revision_new_form.save()
|
|
1542
1690
|
|
|
@@ -1568,9 +1716,9 @@ def part_revision_new(request, part_id):
|
|
|
1568
1716
|
if latest_revision:
|
|
1569
1717
|
messages.info(request, 'New revision automatically incremented to `{}` from your last revision `{}`.'.format(next_revision_number, latest_revision.revision))
|
|
1570
1718
|
latest_revision.revision = next_revision_number # use updated object to populate form but don't save changes
|
|
1571
|
-
part_revision_new_form = PartRevisionNewForm(instance=latest_revision)
|
|
1719
|
+
part_revision_new_form = PartRevisionNewForm(instance=latest_revision, organization=organization)
|
|
1572
1720
|
else:
|
|
1573
|
-
part_revision_new_form = PartRevisionNewForm()
|
|
1721
|
+
part_revision_new_form = PartRevisionNewForm(organization=organization)
|
|
1574
1722
|
|
|
1575
1723
|
return TemplateResponse(request, 'bom/part-revision-new.html', locals())
|
|
1576
1724
|
|
|
@@ -1588,12 +1736,12 @@ def part_revision_edit(request, part_id, part_revision_id):
|
|
|
1588
1736
|
action = reverse('bom:part-revision-edit', kwargs={'part_id': part_id, 'part_revision_id': part_revision_id})
|
|
1589
1737
|
|
|
1590
1738
|
if request.method == 'POST':
|
|
1591
|
-
form = PartRevisionForm(request.POST, instance=part_revision)
|
|
1739
|
+
form = PartRevisionForm(request.POST, instance=part_revision, organization=organization)
|
|
1592
1740
|
if form.is_valid():
|
|
1593
1741
|
form.save()
|
|
1594
1742
|
return HttpResponseRedirect(reverse('bom:part-info', kwargs={'part_id': part_id}))
|
|
1595
1743
|
else:
|
|
1596
|
-
form = PartRevisionForm(instance=part_revision)
|
|
1744
|
+
form = PartRevisionForm(instance=part_revision, organization=organization)
|
|
1597
1745
|
|
|
1598
1746
|
return TemplateResponse(request, 'bom/part-revision-edit.html', locals())
|
|
1599
1747
|
|
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
bom/__init__.py,sha256=HuvSMR9cYQcppTZGD0XjUVUBtHWwWMh1yMQzk_2wTS4,41
|
|
2
|
-
bom/admin.py,sha256=
|
|
2
|
+
bom/admin.py,sha256=MmCa4X9J5aKNbQTrfyLnDdoIt9MrlS3rI1sN9EkhsIs,5892
|
|
3
3
|
bom/apps.py,sha256=TJMUTSX1h2genPwCq6SN6g1fhrrSmjEXGg2zQFa_ryM,147
|
|
4
|
-
bom/auth_backends.py,sha256=
|
|
4
|
+
bom/auth_backends.py,sha256=Xj3MCUJg3r9M4v1c8wrxC3lwpE5F8_2_HD5s9M_5Rms,1480
|
|
5
5
|
bom/base_classes.py,sha256=CrWD7wlIkwYb90VoGcVOcw2WoQszRrCje-Re5d_UW1Q,1183
|
|
6
|
-
bom/constants.py,sha256=
|
|
6
|
+
bom/constants.py,sha256=Vs3-s1_ej8hDMlq5YC01vZXs1TECuPe9sn834UierLI,4666
|
|
7
7
|
bom/context_processors.py,sha256=OxMVCqxGtRoHR7aJfTs6xANpxJQzBDUIuNTG1f_Xjoo,312
|
|
8
|
-
bom/csv_headers.py,sha256=
|
|
8
|
+
bom/csv_headers.py,sha256=0WLvgchbTtW_9ya3UC_qL5hv7muuklf_vtoMNBFAOoc,11468
|
|
9
9
|
bom/decorators.py,sha256=fCK-lxJTBp3LEdZtKMyQg5hRqxQm5RJny9eb7oyjbtE,1233
|
|
10
10
|
bom/form_fields.py,sha256=tLqchl0j8izBCZnm82NMyHpQV6EuGgCjCl5lrnGR2V0,2816
|
|
11
|
-
bom/forms.py,sha256=
|
|
12
|
-
bom/helpers.py,sha256=
|
|
11
|
+
bom/forms.py,sha256=_hsEdhyr2QDQ-Ei7ZRcv4N9s4lmbYR4xT5-NhyJ8rB4,57312
|
|
12
|
+
bom/helpers.py,sha256=jzwNDgD-4htdJfWWjNy8ZhVqfdgSxhNpsFn6kDpC5UU,20172
|
|
13
13
|
bom/local_settings.py,sha256=yE4aupIquCWsFms44qoCrRrlIyM3sqpOkiwyj1WLxI8,820
|
|
14
|
-
bom/models.py,sha256=
|
|
14
|
+
bom/models.py,sha256=dzvhU68hu5Wam4RvD3e75dcsqsbVmBWVe8vi57w40hQ,38424
|
|
15
15
|
bom/part_bom.py,sha256=30HYAKAEhtadiM9tk6vgCQnn7gNJeuXbzF5gXvMvKG4,8720
|
|
16
|
-
bom/settings.py,sha256=
|
|
17
|
-
bom/tests.py,sha256=
|
|
18
|
-
bom/urls.py,sha256=
|
|
16
|
+
bom/settings.py,sha256=aBg0PHaK9NvNZNmfnPrU4-kp2GQeeAGMB_EVvMoAmJs,8197
|
|
17
|
+
bom/tests.py,sha256=Grn2aoXR7AnoVJ9Wa_stgkofk94WS7sPcVKWxpelvY0,73980
|
|
18
|
+
bom/urls.py,sha256=7yjQ7ONydqZHHFHd9jWThEu_03r3LyDcVJ-BGYNy6LQ,7589
|
|
19
19
|
bom/utils.py,sha256=z_2jACSkRc0hsc0mdR8tOK10KiSDeM0a6rXIpztPDuA,7302
|
|
20
20
|
bom/validators.py,sha256=tiQYIblITCOwb8m1kfs_CFmFnJofCtFzqENlxBuGL7Y,872
|
|
21
21
|
bom/wsgi.py,sha256=-2oAfSSVdNFCjxaKXcPp-JL_K8AhVClzls6OC_ZI1Jc,380
|
|
@@ -69,12 +69,14 @@ bom/migrations/0047_sellerpart_seller_part_number.py,sha256=QdjdWMNlSyLHB7uSTq_8
|
|
|
69
69
|
bom/migrations/0048_rename_part_organization_number_class_bom_part_organiz_b333d6_idx_and_more.py,sha256=rZ_mfd_xFu4BglhYxkNuQVqwThZ721kjrlCAn9LkNRo,50969
|
|
70
70
|
bom/migrations/0049_alter_assembly_id_alter_assemblysubparts_id_and_more.py,sha256=l1q5BCNVYWD6Ngf9pGzYq1hQMvvshmdFlm33On63YNc,3247
|
|
71
71
|
bom/migrations/0050_alter_organization_options.py,sha256=n-YGAoUdUxYdh5NY0Zpz2T4CWEOR7tDjSFFk-KZD_tw,432
|
|
72
|
+
bom/migrations/0051_alter_manufacturer_organization_and_more.py,sha256=xjnkZhEgsJDsfK9leBPxxQ2oG_Qf2FdrttTx-lldmjw,1539
|
|
73
|
+
bom/migrations/0052_remove_partrevision_attribute_and_more.py,sha256=dD-wYk2VUkFJvyeJZTftcb60qS48c1foXUh9NkFgbiE,31148
|
|
72
74
|
bom/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
75
|
bom/static/bom/css/dashboard.css,sha256=saLXUpnVRjsV9HNdsmQD4Eq-zBlm8R2YePhvoc1uifk,245
|
|
74
76
|
bom/static/bom/css/jquery.treetable.css,sha256=H37aGBAAFP3R6v08nui9gKSdLE2VGsGsmlttrIImzfE,652
|
|
75
77
|
bom/static/bom/css/materialize.min.css,sha256=OweaP_Ic6rsV-lysfyS4h-LM6sRwuO3euTYfr6M124g,141841
|
|
76
78
|
bom/static/bom/css/part-info.css,sha256=xQ6zJXHLStwU76UVoYxjG-MjdQzFRwg-jANqrj_KFS0,252
|
|
77
|
-
bom/static/bom/css/style.css,sha256=
|
|
79
|
+
bom/static/bom/css/style.css,sha256=NARNKKTNIE-3CU2Glcqbs8fzfedRhbRzIFSRza_bT6I,6737
|
|
78
80
|
bom/static/bom/css/tablesorter-theme.materialize.css,sha256=S7DYXb4vdqdDSUouZ8aIbAxIpemhIzFfeRySdnvvSlc,7435
|
|
79
81
|
bom/static/bom/css/treetable-theme.css,sha256=RxMklK-XfcF90gxekH3IULmyr7_HRA0TdNz_9Xjxuro,24013
|
|
80
82
|
bom/static/bom/doc/sample_part_classes.csv,sha256=nAWhBV9KtHSejLUKD7OKKtbkfUYCeLgCwze9BPstiFo,1694
|
|
@@ -122,6 +124,7 @@ bom/static/bom/img/google/web/vector/btn_google_light_normal_ios.eps,sha256=xZ8g
|
|
|
122
124
|
bom/static/bom/img/google/web/vector/btn_google_light_normal_ios.svg,sha256=Rk6WGzHe0lGJGEyWiN6lTusosfK8ubtqSdf0ZzuWLBE,4358
|
|
123
125
|
bom/static/bom/img/google/web/vector/btn_google_light_pressed_ios.eps,sha256=WUvJDjV62btGu5W1SkKE6juGMOHnA62JJ_0p8C_eOnw,26107
|
|
124
126
|
bom/static/bom/img/google/web/vector/btn_google_light_pressed_ios.svg,sha256=CR9QqAnt_NVL2YweEZe4fN6kQ6QQ31nZHxSmwqVs1t0,4360
|
|
127
|
+
bom/static/bom/js/formset-handler.js,sha256=eEFRa1Sln5Uj7P660_nLDcIHr-mri3SewB89Pr0CwPA,2389
|
|
125
128
|
bom/static/bom/js/jquery-3.4.1.min.js,sha256=CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo,88145
|
|
126
129
|
bom/static/bom/js/jquery.ba-floatingscrollbar.min.js,sha256=NptA5rS2zjhkcu26WQJtMr34psHYzUYRsLrSziTusgg,1271
|
|
127
130
|
bom/static/bom/js/jquery.treetable.js,sha256=JEXnh_LuKYxk8CUIT1anQ4es4nfOqC0bds_NjsbMBUI,16611
|
|
@@ -143,16 +146,17 @@ bom/templates/bom/create-part.html,sha256=UjHx6rkPJl-BRt9gKs5zB3s5Y4vrImDTuD25lv
|
|
|
143
146
|
bom/templates/bom/dashboard-menu.html,sha256=4MnFSw0x4w7R0CsSwEDFP3FuZVBFxVChIHr58l3rpSk,799
|
|
144
147
|
bom/templates/bom/dashboard.html,sha256=m-jGdf4aeIcQqbdqSTwzRCIoLldiIIUQrfM-zRJgyvU,16314
|
|
145
148
|
bom/templates/bom/edit-manufacturer-part.html,sha256=4WagGptcVtYCxW1zWvHGeaZSc-0qqEKDRHFyKarqIjU,3518
|
|
146
|
-
bom/templates/bom/edit-part-class.html,sha256=
|
|
149
|
+
bom/templates/bom/edit-part-class.html,sha256=1DV8_bQiYuxYUuqMT2SsKDI4UWnnZnEx5EU7Nw7KJlw,5757
|
|
147
150
|
bom/templates/bom/edit-part.html,sha256=bGfBI3X8eb7iLy_nYDPQRP2CKyp4tNO2-vjakx-_YiM,3051
|
|
148
|
-
bom/templates/bom/edit-
|
|
151
|
+
bom/templates/bom/edit-quantity-of-measure.html,sha256=79ftu8klYrryj3JrSPEQ3Up5NLh2Qbf4qHp_8qZm5aM,5975
|
|
152
|
+
bom/templates/bom/edit-user-meta.html,sha256=Z4iMA0OErrvVNHbFjLuq7tpO5f6C9ovDpI8-zhC6oX0,3489
|
|
149
153
|
bom/templates/bom/help.html,sha256=hi16fwtVqeWP3Vb3vOpLSKx0ioB1MODJ8lkh6bczfiM,83247
|
|
150
154
|
bom/templates/bom/manufacturer-info.html,sha256=jR98qXONqquEeda92djQgmFfmJiC8jbsGgyXuzJY100,3742
|
|
151
155
|
bom/templates/bom/manufacturers.html,sha256=3ZF8Up-IiAvR6CCmrj_92qPPh7vKrVQaEN05KKX2yrA,5550
|
|
152
156
|
bom/templates/bom/nothing-to-see.html,sha256=cRspNAHlSfv7KjQwp34gANhVqQbXzfFpqtRSm6NoV9s,657
|
|
153
|
-
bom/templates/bom/organization-create.html,sha256=
|
|
154
|
-
bom/templates/bom/part-info.html,sha256=
|
|
155
|
-
bom/templates/bom/part-revision-display.html,sha256=
|
|
157
|
+
bom/templates/bom/organization-create.html,sha256=yLrEJH8BlD-W1USk8pYAzTvr-Qw9ZM5rBGYO3n6lCu0,7982
|
|
158
|
+
bom/templates/bom/part-info.html,sha256=236TZ6yTvR5LCravYiuKgzL_CB6nXrd0_C299gvQF8U,26051
|
|
159
|
+
bom/templates/bom/part-revision-display.html,sha256=f5o-9cjGBz7KPp8ubSiUiFNa2tWzHqSKSe3rC7smR6A,1414
|
|
156
160
|
bom/templates/bom/part-revision-edit.html,sha256=gOWiRd8Vq0z912_fI9UtBC0yYkcF_lruMQfAWN4kqw0,1624
|
|
157
161
|
bom/templates/bom/part-revision-manage-bom.html,sha256=Nd1CIANnCwYU5KzijlIDfKTXjXqrDOeuwmGOk-CYyrY,5073
|
|
158
162
|
bom/templates/bom/part-revision-new.html,sha256=BMgKBNmFkrsimqSeviP1Rn3huOB_0kfK-77oIyqwItw,2691
|
|
@@ -160,7 +164,7 @@ bom/templates/bom/part-revision-release.html,sha256=voG7wmYc1Cm3e_H1IasvQcPuyqnn
|
|
|
160
164
|
bom/templates/bom/search-help.html,sha256=Wh_tXBJtz0bznk0F1C7OSdRhMe2qpOs9NMCBb2i0CFI,4398
|
|
161
165
|
bom/templates/bom/seller-info.html,sha256=MACsHMYQXMWfRslXuvh9hD2z28VXzVi0DSy4yg7WQMk,3595
|
|
162
166
|
bom/templates/bom/sellers.html,sha256=6ut7LwRMGUKYB4BRjiSpDBP9BGgqT7nxpNQpUVWDvkw,5412
|
|
163
|
-
bom/templates/bom/settings.html,sha256=
|
|
167
|
+
bom/templates/bom/settings.html,sha256=g7PhrtbQSf7VRK6pDTPUxUr-PjCLzrJyUErno2pDekY,36184
|
|
164
168
|
bom/templates/bom/signup.html,sha256=tB_x7q3IufSNXsd9Dfh8fdWpkiWSGH2_Zgw749B1PaU,884
|
|
165
169
|
bom/templates/bom/subscription_panel.html,sha256=Ute49APwiXONQW2z0AApJRaSwnwtsYt3_opn0bW5BX8,843
|
|
166
170
|
bom/templates/bom/table_of_contents.html,sha256=7wXWOfmVkk5Itjax5x1PE-g5QjxqmYBr7RW8NgtGRng,1763
|
|
@@ -178,10 +182,10 @@ bom/third_party_apis/google_drive.py,sha256=8ECE9_8f1KAyNTtkalws-Gl51wxAaTLP40bD
|
|
|
178
182
|
bom/third_party_apis/mouser.py,sha256=q2-p0k2n-LNel_QRlfak0kAXT-9hh59k_Pt51PTG09s,5576
|
|
179
183
|
bom/third_party_apis/test_apis.py,sha256=2W0jtTisGTmktC7l556pn9-pZYseTQmmQfo6_4uP4Dc,679
|
|
180
184
|
bom/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
181
|
-
bom/views/json_views.py,sha256=
|
|
182
|
-
bom/views/views.py,sha256=
|
|
183
|
-
django_bom-1.
|
|
184
|
-
django_bom-1.
|
|
185
|
-
django_bom-1.
|
|
186
|
-
django_bom-1.
|
|
187
|
-
django_bom-1.
|
|
185
|
+
bom/views/json_views.py,sha256=LK3-njLZrILLqZxCuE-_sUEC2z2GBxQFRysX67-h14c,2334
|
|
186
|
+
bom/views/views.py,sha256=rYEtIPEJMu1rFSNUARcBlJUe8NgBKoHxl38VWYb8htY,80320
|
|
187
|
+
django_bom-1.257.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
188
|
+
django_bom-1.257.dist-info/METADATA,sha256=50SYINtL9U2pkOe6ypyXI4JCdb5i3GYG2mnOV7bzQ00,7558
|
|
189
|
+
django_bom-1.257.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
190
|
+
django_bom-1.257.dist-info/top_level.txt,sha256=6zytg4lnnobI96dO-ZEadPOCslrrFmf4t2Pnv-y8x0Y,4
|
|
191
|
+
django_bom-1.257.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|