django-bom 1.243__py3-none-any.whl → 1.252__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 +19 -13
- bom/auth_backends.py +3 -1
- bom/forms.py +6 -25
- bom/migrations/0051_alter_manufacturer_organization_and_more.py +41 -0
- bom/models.py +62 -19
- bom/settings.py +2 -0
- bom/templates/bom/organization-create.html +6 -3
- bom/templates/bom/settings.html +4 -4
- bom/views/json_views.py +2 -2
- bom/views/views.py +15 -4
- {django_bom-1.243.dist-info → django_bom-1.252.dist-info}/METADATA +1 -1
- {django_bom-1.243.dist-info → django_bom-1.252.dist-info}/RECORD +15 -14
- {django_bom-1.243.dist-info → django_bom-1.252.dist-info}/WHEEL +0 -0
- {django_bom-1.243.dist-info → django_bom-1.252.dist-info}/licenses/LICENSE +0 -0
- {django_bom-1.243.dist-info → django_bom-1.252.dist-info}/top_level.txt +0 -0
bom/admin.py
CHANGED
|
@@ -6,29 +6,27 @@ from .models import (
|
|
|
6
6
|
Assembly,
|
|
7
7
|
Manufacturer,
|
|
8
8
|
ManufacturerPart,
|
|
9
|
-
Organization,
|
|
10
9
|
Part,
|
|
11
10
|
PartClass,
|
|
12
11
|
PartRevision,
|
|
13
12
|
Seller,
|
|
14
13
|
SellerPart,
|
|
15
14
|
Subpart,
|
|
16
|
-
|
|
15
|
+
get_organization_model,
|
|
16
|
+
get_user_meta_model
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
-
|
|
20
19
|
User = get_user_model()
|
|
20
|
+
UserMeta = get_user_meta_model()
|
|
21
|
+
Organization = get_organization_model()
|
|
21
22
|
|
|
22
23
|
class UserMetaInline(admin.TabularInline):
|
|
23
24
|
model = UserMeta
|
|
25
|
+
verbose_name = 'BOM User Meta'
|
|
24
26
|
raw_id_fields = ('organization',)
|
|
25
27
|
can_delete = False
|
|
26
28
|
|
|
27
29
|
|
|
28
|
-
class UserAdmin(UserAdmin):
|
|
29
|
-
inlines = (UserMetaInline,)
|
|
30
|
-
|
|
31
|
-
|
|
32
30
|
class OrganizationAdmin(admin.ModelAdmin):
|
|
33
31
|
list_display = ('name',)
|
|
34
32
|
|
|
@@ -142,13 +140,21 @@ class AssemblyAdmin(admin.ModelAdmin):
|
|
|
142
140
|
]
|
|
143
141
|
|
|
144
142
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
143
|
+
current_admin = admin.site._registry.get(User)
|
|
144
|
+
|
|
145
|
+
if current_admin:
|
|
146
|
+
admin_class = current_admin.__class__
|
|
147
|
+
inlines = list(admin_class.inlines or [])
|
|
148
|
+
if UserMetaInline not in inlines:
|
|
149
|
+
inlines.append(UserMetaInline)
|
|
150
|
+
admin_class.inlines = inlines
|
|
151
|
+
else:
|
|
152
|
+
class BomUserAdmin(UserAdmin):
|
|
153
|
+
inlines = [UserMetaInline]
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
admin.site.register(User, BomUserAdmin)
|
|
150
157
|
|
|
151
|
-
admin.site.register(User, UserAdmin)
|
|
152
158
|
admin.site.register(Organization, OrganizationAdmin)
|
|
153
159
|
admin.site.register(Seller, SellerAdmin)
|
|
154
160
|
admin.site.register(SellerPart, SellerPartAdmin)
|
bom/auth_backends.py
CHANGED
bom/forms.py
CHANGED
|
@@ -1,41 +1,27 @@
|
|
|
1
1
|
import codecs
|
|
2
2
|
import csv
|
|
3
3
|
import logging
|
|
4
|
-
from typing import Type, TypeVar
|
|
5
4
|
|
|
6
5
|
from django import forms
|
|
7
6
|
from django.contrib.auth.forms import UserCreationForm
|
|
8
7
|
from django.core.exceptions import ValidationError
|
|
9
|
-
from django.core.validators import MaxLengthValidator,
|
|
8
|
+
from django.core.validators import MaxLengthValidator, MinLengthValidator
|
|
10
9
|
from django.db import IntegrityError
|
|
11
10
|
from django.forms.models import model_to_dict
|
|
12
11
|
from django.utils.translation import gettext_lazy as _
|
|
13
|
-
|
|
14
12
|
from djmoney.money import Money
|
|
15
13
|
|
|
16
14
|
from .constants import (
|
|
17
|
-
CONFIGURATION_TYPES,
|
|
18
15
|
CURRENT_UNITS,
|
|
19
16
|
DISTANCE_UNITS,
|
|
20
17
|
FREQUENCY_UNITS,
|
|
21
18
|
INTERFACE_TYPES,
|
|
22
19
|
MEMORY_UNITS,
|
|
23
|
-
NUMBER_CLASS_CODE_LEN_DEFAULT,
|
|
24
|
-
NUMBER_CLASS_CODE_LEN_MAX,
|
|
25
|
-
NUMBER_CLASS_CODE_LEN_MIN,
|
|
26
|
-
NUMBER_ITEM_LEN_DEFAULT,
|
|
27
|
-
NUMBER_ITEM_LEN_MAX,
|
|
28
|
-
NUMBER_ITEM_LEN_MIN,
|
|
29
20
|
NUMBER_SCHEME_INTELLIGENT,
|
|
30
21
|
NUMBER_SCHEME_SEMI_INTELLIGENT,
|
|
31
|
-
NUMBER_VARIATION_LEN_DEFAULT,
|
|
32
|
-
NUMBER_VARIATION_LEN_MAX,
|
|
33
|
-
NUMBER_VARIATION_LEN_MIN,
|
|
34
22
|
PACKAGE_TYPES,
|
|
35
23
|
POWER_UNITS,
|
|
36
24
|
ROLE_TYPE_VIEWER,
|
|
37
|
-
ROLE_TYPES,
|
|
38
|
-
SUBSCRIPTION_TYPES,
|
|
39
25
|
TEMPERATURE_UNITS,
|
|
40
26
|
VALUE_UNITS,
|
|
41
27
|
VOLTAGE_UNITS,
|
|
@@ -43,11 +29,9 @@ from .constants import (
|
|
|
43
29
|
WEIGHT_UNITS,
|
|
44
30
|
)
|
|
45
31
|
from .csv_headers import (
|
|
46
|
-
BOMFlatCSVHeaders,
|
|
47
32
|
BOMIndentedCSVHeaders,
|
|
48
33
|
CSVHeaderError,
|
|
49
34
|
PartClassesCSVHeaders,
|
|
50
|
-
PartsListCSVHeaders,
|
|
51
35
|
)
|
|
52
36
|
from .form_fields import AutocompleteTextInput
|
|
53
37
|
from .models import (
|
|
@@ -55,7 +39,6 @@ from .models import (
|
|
|
55
39
|
AssemblySubparts,
|
|
56
40
|
Manufacturer,
|
|
57
41
|
ManufacturerPart,
|
|
58
|
-
Organization,
|
|
59
42
|
Part,
|
|
60
43
|
PartClass,
|
|
61
44
|
PartRevision,
|
|
@@ -63,20 +46,18 @@ from .models import (
|
|
|
63
46
|
SellerPart,
|
|
64
47
|
Subpart,
|
|
65
48
|
User,
|
|
66
|
-
|
|
49
|
+
get_user_meta_model,
|
|
50
|
+
get_organization_model,
|
|
67
51
|
)
|
|
68
52
|
from .utils import (
|
|
69
|
-
check_references_for_duplicates,
|
|
70
|
-
get_from_dict,
|
|
71
53
|
listify_string,
|
|
72
|
-
prep_for_sorting_nicely,
|
|
73
54
|
stringify_list,
|
|
74
55
|
)
|
|
75
|
-
from .validators import alphanumeric
|
|
76
|
-
|
|
56
|
+
from .validators import alphanumeric
|
|
77
57
|
|
|
78
58
|
logger = logging.getLogger(__name__)
|
|
79
|
-
|
|
59
|
+
Organization = get_organization_model()
|
|
60
|
+
UserMeta = get_user_meta_model()
|
|
80
61
|
|
|
81
62
|
class UserModelChoiceField(forms.ModelChoiceField):
|
|
82
63
|
def label_from_instance(self, user):
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Generated by Django 5.2.8 on 2026-01-04 00:59
|
|
2
|
+
|
|
3
|
+
import django.db.models.deletion
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
from django.db import migrations, models
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Migration(migrations.Migration):
|
|
9
|
+
|
|
10
|
+
dependencies = [
|
|
11
|
+
('bom', '0050_alter_organization_options'),
|
|
12
|
+
migrations.swappable_dependency(settings.BOM_ORGANIZATION_MODEL),
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
operations = [
|
|
16
|
+
migrations.AlterField(
|
|
17
|
+
model_name='manufacturer',
|
|
18
|
+
name='organization',
|
|
19
|
+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.BOM_ORGANIZATION_MODEL),
|
|
20
|
+
),
|
|
21
|
+
migrations.AlterField(
|
|
22
|
+
model_name='part',
|
|
23
|
+
name='organization',
|
|
24
|
+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.BOM_ORGANIZATION_MODEL),
|
|
25
|
+
),
|
|
26
|
+
migrations.AlterField(
|
|
27
|
+
model_name='partclass',
|
|
28
|
+
name='organization',
|
|
29
|
+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.BOM_ORGANIZATION_MODEL),
|
|
30
|
+
),
|
|
31
|
+
migrations.AlterField(
|
|
32
|
+
model_name='seller',
|
|
33
|
+
name='organization',
|
|
34
|
+
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.BOM_ORGANIZATION_MODEL),
|
|
35
|
+
),
|
|
36
|
+
migrations.AlterField(
|
|
37
|
+
model_name='usermeta',
|
|
38
|
+
name='organization',
|
|
39
|
+
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.BOM_ORGANIZATION_MODEL),
|
|
40
|
+
),
|
|
41
|
+
]
|
bom/models.py
CHANGED
|
@@ -2,6 +2,7 @@ from __future__ import unicode_literals
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
|
|
5
|
+
from django.apps import apps
|
|
5
6
|
from django.conf import settings
|
|
6
7
|
from django.contrib.auth import get_user_model
|
|
7
8
|
from django.core.cache import cache
|
|
@@ -23,32 +24,54 @@ logger = logging.getLogger(__name__)
|
|
|
23
24
|
User = get_user_model()
|
|
24
25
|
|
|
25
26
|
|
|
27
|
+
def get_user_meta_model():
|
|
28
|
+
from django.apps import apps
|
|
29
|
+
from django.conf import settings
|
|
30
|
+
return apps.get_model(settings.BOM_USER_META_MODEL)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_organization_model():
|
|
34
|
+
from django.apps import apps
|
|
35
|
+
from django.conf import settings
|
|
36
|
+
return apps.get_model(settings.BOM_ORGANIZATION_MODEL)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _user_meta(self, organization=None):
|
|
40
|
+
from django.apps import apps
|
|
41
|
+
from django.conf import settings
|
|
42
|
+
UserMetaModel = apps.get_model(settings.BOM_USER_META_MODEL)
|
|
43
|
+
meta, created = UserMetaModel.objects.get_or_create(
|
|
44
|
+
user=self,
|
|
45
|
+
defaults={'organization': organization}
|
|
46
|
+
)
|
|
47
|
+
return meta
|
|
48
|
+
|
|
49
|
+
|
|
26
50
|
class OrganizationScopedModel(models.Model):
|
|
27
|
-
organization = models.ForeignKey(
|
|
51
|
+
organization = models.ForeignKey(settings.BOM_ORGANIZATION_MODEL, on_delete=models.CASCADE, db_index=True)
|
|
28
52
|
|
|
29
53
|
class Meta:
|
|
30
54
|
abstract = True
|
|
31
55
|
|
|
32
56
|
|
|
33
|
-
class
|
|
57
|
+
class AbstractOrganization(models.Model):
|
|
34
58
|
name = models.CharField(max_length=255, default=None)
|
|
35
|
-
subscription = models.CharField(max_length=1, choices=SUBSCRIPTION_TYPES)
|
|
36
|
-
subscription_quantity = models.IntegerField(default=0)
|
|
37
59
|
owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
|
38
60
|
number_scheme = models.CharField(max_length=1, choices=NUMBER_SCHEMES, default=NUMBER_SCHEME_SEMI_INTELLIGENT)
|
|
39
61
|
number_class_code_len = models.PositiveIntegerField(default=NUMBER_CLASS_CODE_LEN_DEFAULT,
|
|
40
|
-
validators=[MinValueValidator(NUMBER_CLASS_CODE_LEN_MIN),
|
|
62
|
+
validators=[MinValueValidator(NUMBER_CLASS_CODE_LEN_MIN),
|
|
63
|
+
MaxValueValidator(NUMBER_CLASS_CODE_LEN_MAX)])
|
|
41
64
|
number_item_len = models.PositiveIntegerField(default=NUMBER_ITEM_LEN_DEFAULT,
|
|
42
|
-
validators=[MinValueValidator(NUMBER_ITEM_LEN_MIN),
|
|
65
|
+
validators=[MinValueValidator(NUMBER_ITEM_LEN_MIN),
|
|
66
|
+
MaxValueValidator(NUMBER_ITEM_LEN_MAX)])
|
|
43
67
|
number_variation_len = models.PositiveIntegerField(default=NUMBER_VARIATION_LEN_DEFAULT,
|
|
44
|
-
validators=[MinValueValidator(NUMBER_VARIATION_LEN_MIN),
|
|
68
|
+
validators=[MinValueValidator(NUMBER_VARIATION_LEN_MIN),
|
|
69
|
+
MaxValueValidator(NUMBER_VARIATION_LEN_MAX)])
|
|
45
70
|
google_drive_parent = models.CharField(max_length=128, blank=True, default=None, null=True)
|
|
46
71
|
currency = CurrencyField(max_length=3, choices=CURRENCY_CHOICES, default='USD')
|
|
47
72
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
("manage_members", "Can manage organization members"),
|
|
51
|
-
)
|
|
73
|
+
subscription = models.CharField(max_length=1, choices=SUBSCRIPTION_TYPES)
|
|
74
|
+
subscription_quantity = models.IntegerField(default=0)
|
|
52
75
|
|
|
53
76
|
def number_cs(self):
|
|
54
77
|
return "C" * self.number_class_code_len
|
|
@@ -76,13 +99,25 @@ class Organization(models.Model):
|
|
|
76
99
|
return self.owner.email
|
|
77
100
|
|
|
78
101
|
def save(self, *args, **kwargs):
|
|
79
|
-
super(
|
|
80
|
-
SellerPart.objects.filter(seller__organization=self).update(unit_cost_currency=self.currency,
|
|
102
|
+
super(AbstractOrganization, self).save()
|
|
103
|
+
SellerPart.objects.filter(seller__organization=self).update(unit_cost_currency=self.currency,
|
|
104
|
+
nre_cost_currency=self.currency)
|
|
81
105
|
|
|
106
|
+
class Meta:
|
|
107
|
+
abstract = True
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class Organization(AbstractOrganization):
|
|
111
|
+
class Meta:
|
|
112
|
+
swappable = 'BOM_ORGANIZATION_MODEL'
|
|
113
|
+
permissions = (
|
|
114
|
+
("manage_members", "Can manage organization members"),
|
|
115
|
+
)
|
|
82
116
|
|
|
83
|
-
|
|
117
|
+
|
|
118
|
+
class AbstractUserMeta(models.Model):
|
|
84
119
|
user = models.OneToOneField(settings.AUTH_USER_MODEL, db_index=True, on_delete=models.CASCADE)
|
|
85
|
-
organization = models.ForeignKey(
|
|
120
|
+
organization = models.ForeignKey(settings.BOM_ORGANIZATION_MODEL, blank=True, null=True, on_delete=models.CASCADE)
|
|
86
121
|
role = models.CharField(max_length=1, choices=ROLE_TYPES)
|
|
87
122
|
|
|
88
123
|
def get_or_create_organization(self):
|
|
@@ -92,7 +127,9 @@ class UserMeta(models.Model):
|
|
|
92
127
|
else:
|
|
93
128
|
org_name = self.user.first_name + ' ' + self.user.last_name
|
|
94
129
|
|
|
95
|
-
|
|
130
|
+
OrganizationModel = apps.get_model(settings.BOM_ORGANIZATION_MODEL)
|
|
131
|
+
organization, created = OrganizationModel.objects.get_or_create(owner=self.user, defaults={'name': org_name,
|
|
132
|
+
'subscription': 'F'})
|
|
96
133
|
|
|
97
134
|
self.organization = organization
|
|
98
135
|
self.role = 'A'
|
|
@@ -109,10 +146,13 @@ class UserMeta(models.Model):
|
|
|
109
146
|
def is_organization_owner(self) -> bool:
|
|
110
147
|
return self.organization.owner == self.user if self.organization else False
|
|
111
148
|
|
|
112
|
-
|
|
113
|
-
|
|
149
|
+
class Meta:
|
|
150
|
+
abstract = True
|
|
114
151
|
|
|
115
|
-
|
|
152
|
+
|
|
153
|
+
class UserMeta(AbstractUserMeta):
|
|
154
|
+
class Meta:
|
|
155
|
+
swappable = 'BOM_USER_META_MODEL'
|
|
116
156
|
|
|
117
157
|
|
|
118
158
|
class PartClass(OrganizationScopedModel):
|
|
@@ -730,3 +770,6 @@ class SellerPart(models.Model, AsDictModel):
|
|
|
730
770
|
|
|
731
771
|
def __str__(self):
|
|
732
772
|
return u'%s' % (self.manufacturer_part.part.full_part_number() + ' ' + self.seller.name)
|
|
773
|
+
|
|
774
|
+
|
|
775
|
+
User.add_to_class('bom_profile', _user_meta)
|
bom/settings.py
CHANGED
|
@@ -40,9 +40,12 @@
|
|
|
40
40
|
|
|
41
41
|
<div id="join-organization" style="display: none;">
|
|
42
42
|
<h3 class="center">Join an existing Organization</h3>
|
|
43
|
-
<h5 class="center" style="padding-top: 36px;">To join an existing organization, you must provide
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
<h5 class="center" style="padding-top: 36px;">To join an existing organization, you must provide your
|
|
44
|
+
username (not e-mail) to your IndaBOM organization owner. <br><br>Your username is
|
|
45
|
+
<b>{{ user.username }}</b>
|
|
46
|
+
click
|
|
47
|
+
<a href="mailto:?subject=Add me to your IndaBOM Organization&body=Hi,%0D%0A%0D%0APlease add me to your IndaBOM organization. To do so, log in, go to Settings > Organization and add me via my username {{ user.username }}">here</a>
|
|
48
|
+
to send an e-mail to your organization owner with instructions.</h5>
|
|
46
49
|
<br><br>
|
|
47
50
|
<p class="center">Oops! I meant to <a class="modal-trigger" onclick="resetView()">create a new organization</a>.</p>
|
|
48
51
|
</div>
|
bom/templates/bom/settings.html
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
{% block content %}
|
|
9
9
|
<div class="row container-app">
|
|
10
|
-
<div class="col
|
|
10
|
+
<div class="col s12 l8 offset-l2">
|
|
11
11
|
<ul id="tabs" class="tabs tabs-fixed-width">
|
|
12
12
|
<li class="tab"><a id="user-tab" href="#user">User</a></li>
|
|
13
13
|
<li class="tab"><a id="indabom-tab" href="#indabom">IndaBOM</a></li>
|
|
@@ -242,6 +242,9 @@
|
|
|
242
242
|
</form>
|
|
243
243
|
</div>
|
|
244
244
|
|
|
245
|
+
{# Subscription & Billing placed after Users for a more natural flow #}
|
|
246
|
+
{% include 'bom/subscription_panel.html' %}
|
|
247
|
+
|
|
245
248
|
<div class="section">
|
|
246
249
|
<h4 class="section-title"><i class="material-icons teal-text text-darken-1">group</i>Users</h4>
|
|
247
250
|
<form name="seller" action="{% url 'bom:settings' tab_anchor=ORGANIZATION_TAB %}" method="post"
|
|
@@ -301,9 +304,6 @@
|
|
|
301
304
|
</form>
|
|
302
305
|
</div>
|
|
303
306
|
|
|
304
|
-
{# Subscription & Billing placed after Users for a more natural flow #}
|
|
305
|
-
{% include 'bom/subscription_panel.html' %}
|
|
306
|
-
|
|
307
307
|
<div class="section">
|
|
308
308
|
<h4 class="section-title"><i
|
|
309
309
|
class="material-icons teal-text text-darken-1">integration_instructions</i>Integrations</h4>
|
bom/views/json_views.py
CHANGED
|
@@ -5,9 +5,9 @@ from django.shortcuts import get_object_or_404
|
|
|
5
5
|
from django.utils.decorators import method_decorator
|
|
6
6
|
from django.views import View
|
|
7
7
|
|
|
8
|
-
from bom.models import
|
|
9
|
-
from bom.third_party_apis.mouser import Mouser
|
|
8
|
+
from bom.models import PartRevision
|
|
10
9
|
from bom.third_party_apis.base_api import BaseApiError
|
|
10
|
+
from bom.third_party_apis.mouser import Mouser
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class BomJsonResponse(View):
|
bom/views/views.py
CHANGED
|
@@ -69,11 +69,12 @@ from bom.models import (
|
|
|
69
69
|
SellerPart,
|
|
70
70
|
Subpart,
|
|
71
71
|
User,
|
|
72
|
-
|
|
72
|
+
get_user_meta_model
|
|
73
73
|
)
|
|
74
74
|
from bom.utils import check_references_for_duplicates, listify_string, prep_for_sorting_nicely
|
|
75
75
|
|
|
76
76
|
logger = logging.getLogger(__name__)
|
|
77
|
+
UserMeta = get_user_meta_model()
|
|
77
78
|
BOM_LOGIN_URL = getattr(settings, "BOM_LOGIN_URL", None) or settings.LOGIN_URL
|
|
78
79
|
|
|
79
80
|
def form_error_messages(form_errors) -> [str]:
|
|
@@ -400,8 +401,13 @@ def bom_settings(request, tab_anchor=None):
|
|
|
400
401
|
added_user_profile = user_add_form.save()
|
|
401
402
|
messages.info(request, f"Added {added_user_profile.user.first_name} {added_user_profile.user.last_name} to your organization.")
|
|
402
403
|
else:
|
|
403
|
-
|
|
404
|
-
|
|
404
|
+
for field, errors in user_add_form.errors.items():
|
|
405
|
+
for error in errors:
|
|
406
|
+
messages.error(request, f"{field.capitalize()}: {error}")
|
|
407
|
+
users_in_organization.all()
|
|
408
|
+
users_in_organization_count = users_in_organization.count()
|
|
409
|
+
has_member_capacity = users_in_organization_count < organization.subscription_quantity
|
|
410
|
+
seats_available = max(organization.subscription_quantity - users_in_organization_count, 0)
|
|
405
411
|
elif 'clear-add-user' in request.POST:
|
|
406
412
|
tab_anchor = ORGANIZATION_TAB
|
|
407
413
|
user_add_form = UserAddForm()
|
|
@@ -421,7 +427,10 @@ def bom_settings(request, tab_anchor=None):
|
|
|
421
427
|
user_meta.save()
|
|
422
428
|
except UserMeta.DoesNotExist:
|
|
423
429
|
messages.error(request, "No user found with given id {}.".format(user_meta_id))
|
|
424
|
-
|
|
430
|
+
users_in_organization.all()
|
|
431
|
+
users_in_organization_count = users_in_organization.count()
|
|
432
|
+
has_member_capacity = users_in_organization_count < organization.subscription_quantity
|
|
433
|
+
seats_available = max(organization.subscription_quantity - users_in_organization_count, 0)
|
|
425
434
|
elif 'submit-edit-organization' in request.POST:
|
|
426
435
|
tab_anchor = ORGANIZATION_TAB
|
|
427
436
|
organization_form = OrganizationFormEditSettings(request.POST, instance=organization, user=user)
|
|
@@ -521,6 +530,8 @@ def bom_settings(request, tab_anchor=None):
|
|
|
521
530
|
profile.save()
|
|
522
531
|
if users_in_organization == 0:
|
|
523
532
|
organization.delete()
|
|
533
|
+
else:
|
|
534
|
+
messages.warning(request, "No action was taken because no form field was submitted.")
|
|
524
535
|
|
|
525
536
|
user_form = UserForm(instance=user)
|
|
526
537
|
user_add_form = UserAddForm()
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
bom/__init__.py,sha256=HuvSMR9cYQcppTZGD0XjUVUBtHWwWMh1yMQzk_2wTS4,41
|
|
2
|
-
bom/admin.py,sha256=
|
|
2
|
+
bom/admin.py,sha256=3fgD0_e5U76P1UT8CTeWbmqo3KK3PKz8ECz-5a_pXEM,4401
|
|
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
6
|
bom/constants.py,sha256=5CFE0uvKTL91w24rqdAB6EMgpIyw0HhfmmktxF4gCgs,4296
|
|
7
7
|
bom/context_processors.py,sha256=OxMVCqxGtRoHR7aJfTs6xANpxJQzBDUIuNTG1f_Xjoo,312
|
|
8
8
|
bom/csv_headers.py,sha256=1VDJ7aNqYjDhf5_rfWBZqO6wsIS99oD01yXx2nNmIHQ,12620
|
|
9
9
|
bom/decorators.py,sha256=fCK-lxJTBp3LEdZtKMyQg5hRqxQm5RJny9eb7oyjbtE,1233
|
|
10
10
|
bom/form_fields.py,sha256=tLqchl0j8izBCZnm82NMyHpQV6EuGgCjCl5lrnGR2V0,2816
|
|
11
|
-
bom/forms.py,sha256=
|
|
11
|
+
bom/forms.py,sha256=wmfurA8M0cxc72nLPsMo0-m6JOXzXOhFF2MSRXzZnVI,68836
|
|
12
12
|
bom/helpers.py,sha256=ONsDM0agG9sKJWMjN4IRNlWx2HNF7T0CXM-ts0GRiAY,15031
|
|
13
13
|
bom/local_settings.py,sha256=yE4aupIquCWsFms44qoCrRrlIyM3sqpOkiwyj1WLxI8,820
|
|
14
|
-
bom/models.py,sha256=
|
|
14
|
+
bom/models.py,sha256=GWnSNEDP5po_fo9CphwtimZTFkSrbjKCs6zg0g-LKD8,37834
|
|
15
15
|
bom/part_bom.py,sha256=30HYAKAEhtadiM9tk6vgCQnn7gNJeuXbzF5gXvMvKG4,8720
|
|
16
|
-
bom/settings.py,sha256=
|
|
16
|
+
bom/settings.py,sha256=aBg0PHaK9NvNZNmfnPrU4-kp2GQeeAGMB_EVvMoAmJs,8197
|
|
17
17
|
bom/tests.py,sha256=ZqcTUYVXeWjAqzKAV6hp6SKTU0_IOTwIEboTujl7N_M,69905
|
|
18
18
|
bom/urls.py,sha256=sGNKO8BsTO_TDPsqB-c_fqRozaNHOf9WYRaOy-7OLAE,6841
|
|
19
19
|
bom/utils.py,sha256=z_2jACSkRc0hsc0mdR8tOK10KiSDeM0a6rXIpztPDuA,7302
|
|
@@ -69,6 +69,7 @@ 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
|
|
72
73
|
bom/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
73
74
|
bom/static/bom/css/dashboard.css,sha256=saLXUpnVRjsV9HNdsmQD4Eq-zBlm8R2YePhvoc1uifk,245
|
|
74
75
|
bom/static/bom/css/jquery.treetable.css,sha256=H37aGBAAFP3R6v08nui9gKSdLE2VGsGsmlttrIImzfE,652
|
|
@@ -150,7 +151,7 @@ bom/templates/bom/help.html,sha256=hi16fwtVqeWP3Vb3vOpLSKx0ioB1MODJ8lkh6bczfiM,8
|
|
|
150
151
|
bom/templates/bom/manufacturer-info.html,sha256=jR98qXONqquEeda92djQgmFfmJiC8jbsGgyXuzJY100,3742
|
|
151
152
|
bom/templates/bom/manufacturers.html,sha256=3ZF8Up-IiAvR6CCmrj_92qPPh7vKrVQaEN05KKX2yrA,5550
|
|
152
153
|
bom/templates/bom/nothing-to-see.html,sha256=cRspNAHlSfv7KjQwp34gANhVqQbXzfFpqtRSm6NoV9s,657
|
|
153
|
-
bom/templates/bom/organization-create.html,sha256=
|
|
154
|
+
bom/templates/bom/organization-create.html,sha256=yLrEJH8BlD-W1USk8pYAzTvr-Qw9ZM5rBGYO3n6lCu0,7982
|
|
154
155
|
bom/templates/bom/part-info.html,sha256=lilYygmR8cQhQ5lJeYK6dj-OdIm72s1SerELHqyrPSs,25406
|
|
155
156
|
bom/templates/bom/part-revision-display.html,sha256=t_wwzf910fhBc2vFjqoISnhX4OEr7pkfh8R-RGq_6ac,5509
|
|
156
157
|
bom/templates/bom/part-revision-edit.html,sha256=gOWiRd8Vq0z912_fI9UtBC0yYkcF_lruMQfAWN4kqw0,1624
|
|
@@ -160,7 +161,7 @@ bom/templates/bom/part-revision-release.html,sha256=voG7wmYc1Cm3e_H1IasvQcPuyqnn
|
|
|
160
161
|
bom/templates/bom/search-help.html,sha256=Wh_tXBJtz0bznk0F1C7OSdRhMe2qpOs9NMCBb2i0CFI,4398
|
|
161
162
|
bom/templates/bom/seller-info.html,sha256=MACsHMYQXMWfRslXuvh9hD2z28VXzVi0DSy4yg7WQMk,3595
|
|
162
163
|
bom/templates/bom/sellers.html,sha256=6ut7LwRMGUKYB4BRjiSpDBP9BGgqT7nxpNQpUVWDvkw,5412
|
|
163
|
-
bom/templates/bom/settings.html,sha256=
|
|
164
|
+
bom/templates/bom/settings.html,sha256=az0QXxrCPEDa8XRG7q3ASDe8dIP3G941qKmijrzwczw,28241
|
|
164
165
|
bom/templates/bom/signup.html,sha256=tB_x7q3IufSNXsd9Dfh8fdWpkiWSGH2_Zgw749B1PaU,884
|
|
165
166
|
bom/templates/bom/subscription_panel.html,sha256=Ute49APwiXONQW2z0AApJRaSwnwtsYt3_opn0bW5BX8,843
|
|
166
167
|
bom/templates/bom/table_of_contents.html,sha256=7wXWOfmVkk5Itjax5x1PE-g5QjxqmYBr7RW8NgtGRng,1763
|
|
@@ -178,10 +179,10 @@ bom/third_party_apis/google_drive.py,sha256=8ECE9_8f1KAyNTtkalws-Gl51wxAaTLP40bD
|
|
|
178
179
|
bom/third_party_apis/mouser.py,sha256=q2-p0k2n-LNel_QRlfak0kAXT-9hh59k_Pt51PTG09s,5576
|
|
179
180
|
bom/third_party_apis/test_apis.py,sha256=2W0jtTisGTmktC7l556pn9-pZYseTQmmQfo6_4uP4Dc,679
|
|
180
181
|
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.
|
|
182
|
+
bom/views/json_views.py,sha256=LK3-njLZrILLqZxCuE-_sUEC2z2GBxQFRysX67-h14c,2334
|
|
183
|
+
bom/views/views.py,sha256=IB0pgdQojvuvykopTdpM42m8EMgy8oYJ4Ui01TR30Ys,73146
|
|
184
|
+
django_bom-1.252.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
185
|
+
django_bom-1.252.dist-info/METADATA,sha256=JCrxqDW9rFFK-tSg056gcNzbkcVU89iuahZ1tiKCK7o,7558
|
|
186
|
+
django_bom-1.252.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
187
|
+
django_bom-1.252.dist-info/top_level.txt,sha256=6zytg4lnnobI96dO-ZEadPOCslrrFmf4t2Pnv-y8x0Y,4
|
|
188
|
+
django_bom-1.252.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|