django-bom 1.237__py3-none-any.whl → 1.238__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/auth_backends.py +45 -0
- bom/models.py +8 -33
- bom/templates/bom/bom-modal-add-users.html +49 -0
- bom/templates/bom/settings.html +8 -18
- bom/views/views.py +8 -4
- {django_bom-1.237.dist-info → django_bom-1.238.dist-info}/METADATA +1 -1
- {django_bom-1.237.dist-info → django_bom-1.238.dist-info}/RECORD +10 -8
- {django_bom-1.237.dist-info → django_bom-1.238.dist-info}/WHEEL +0 -0
- {django_bom-1.237.dist-info → django_bom-1.238.dist-info}/licenses/LICENSE +0 -0
- {django_bom-1.237.dist-info → django_bom-1.238.dist-info}/top_level.txt +0 -0
bom/auth_backends.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from . import constants
|
|
4
|
+
from .models import Organization
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class OrganizationPermissionBackend:
|
|
8
|
+
"""
|
|
9
|
+
Object-level permission backend for django-bom.
|
|
10
|
+
|
|
11
|
+
- Uses Django's has_perm(user, perm, obj) to evaluate permissions tied to an Organization.
|
|
12
|
+
- Superusers are granted all permissions.
|
|
13
|
+
- For `bom.manage_members`: User must be owner or admin within the organization.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def authenticate(self, request, **credentials): # pragma: no cover - not used for auth
|
|
17
|
+
return None
|
|
18
|
+
|
|
19
|
+
def has_perm(self, user_obj, perm: str, obj: Optional[object] = None):
|
|
20
|
+
# Only handle our specific object-level permission. Let other backends process others.
|
|
21
|
+
if not user_obj or not user_obj.is_authenticated:
|
|
22
|
+
return False
|
|
23
|
+
|
|
24
|
+
if user_obj.is_superuser:
|
|
25
|
+
return True
|
|
26
|
+
|
|
27
|
+
if perm != 'bom.manage_members':
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
if obj is None or not isinstance(obj, Organization):
|
|
31
|
+
return False
|
|
32
|
+
|
|
33
|
+
profile = user_obj.bom_profile()
|
|
34
|
+
if not profile or profile.organization_id != obj.id:
|
|
35
|
+
return False
|
|
36
|
+
|
|
37
|
+
if obj.subscription != constants.SUBSCRIPTION_TYPE_PRO:
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
is_owner = obj.owner_id == user_obj.id
|
|
41
|
+
is_admin = getattr(profile, 'role', None) == constants.ROLE_TYPE_ADMIN
|
|
42
|
+
if not (is_owner or is_admin):
|
|
43
|
+
return False
|
|
44
|
+
|
|
45
|
+
return True
|
bom/models.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
from __future__ import unicode_literals
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from math import ceil
|
|
5
4
|
|
|
6
5
|
from django.conf import settings
|
|
7
6
|
from django.contrib.auth import get_user_model
|
|
@@ -9,45 +8,16 @@ from django.core.cache import cache
|
|
|
9
8
|
from django.core.validators import MaxValueValidator, MinValueValidator
|
|
10
9
|
from django.db import models
|
|
11
10
|
from django.utils import timezone
|
|
12
|
-
|
|
13
11
|
from djmoney.models.fields import CURRENCY_CHOICES, CurrencyField, MoneyField
|
|
12
|
+
from math import ceil
|
|
14
13
|
from social_django.models import UserSocialAuth
|
|
15
14
|
|
|
16
15
|
from .base_classes import AsDictModel
|
|
17
|
-
from .constants import
|
|
18
|
-
CONFIGURATION_TYPES,
|
|
19
|
-
CURRENT_UNITS,
|
|
20
|
-
DISTANCE_UNITS,
|
|
21
|
-
FREQUENCY_UNITS,
|
|
22
|
-
INTERFACE_TYPES,
|
|
23
|
-
MEMORY_UNITS,
|
|
24
|
-
NUMBER_CLASS_CODE_LEN_DEFAULT,
|
|
25
|
-
NUMBER_CLASS_CODE_LEN_MAX,
|
|
26
|
-
NUMBER_CLASS_CODE_LEN_MIN,
|
|
27
|
-
NUMBER_ITEM_LEN_DEFAULT,
|
|
28
|
-
NUMBER_ITEM_LEN_MAX,
|
|
29
|
-
NUMBER_ITEM_LEN_MIN,
|
|
30
|
-
NUMBER_SCHEME_INTELLIGENT,
|
|
31
|
-
NUMBER_SCHEME_SEMI_INTELLIGENT,
|
|
32
|
-
NUMBER_SCHEMES,
|
|
33
|
-
NUMBER_VARIATION_LEN_DEFAULT,
|
|
34
|
-
NUMBER_VARIATION_LEN_MAX,
|
|
35
|
-
NUMBER_VARIATION_LEN_MIN,
|
|
36
|
-
PACKAGE_TYPES,
|
|
37
|
-
POWER_UNITS,
|
|
38
|
-
ROLE_TYPES,
|
|
39
|
-
SUBSCRIPTION_TYPES,
|
|
40
|
-
TEMPERATURE_UNITS,
|
|
41
|
-
VALUE_UNITS,
|
|
42
|
-
VOLTAGE_UNITS,
|
|
43
|
-
WAVELENGTH_UNITS,
|
|
44
|
-
WEIGHT_UNITS,
|
|
45
|
-
)
|
|
16
|
+
from .constants import *
|
|
46
17
|
from .csv_headers import PartsListCSVHeaders, PartsListCSVHeadersSemiIntelligent
|
|
47
18
|
from .part_bom import PartBom, PartBomItem, PartIndentedBomItem
|
|
48
19
|
from .utils import increment_str, listify_string, prep_for_sorting_nicely, stringify_list, strip_trailing_zeros
|
|
49
|
-
from .validators import alphanumeric,
|
|
50
|
-
|
|
20
|
+
from .validators import alphanumeric, validate_pct
|
|
51
21
|
|
|
52
22
|
logger = logging.getLogger(__name__)
|
|
53
23
|
User = get_user_model()
|
|
@@ -75,6 +45,11 @@ class Organization(models.Model):
|
|
|
75
45
|
google_drive_parent = models.CharField(max_length=128, blank=True, default=None, null=True)
|
|
76
46
|
currency = CurrencyField(max_length=3, choices=CURRENCY_CHOICES, default='USD')
|
|
77
47
|
|
|
48
|
+
class Meta:
|
|
49
|
+
permissions = (
|
|
50
|
+
("manage_members", "Can manage organization members"),
|
|
51
|
+
)
|
|
52
|
+
|
|
78
53
|
def number_cs(self):
|
|
79
54
|
return "C" * self.number_class_code_len
|
|
80
55
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{% load materializecss %}
|
|
2
|
+
{% load static %}
|
|
3
|
+
|
|
4
|
+
<a class="waves-effect waves-light btn btn-primary modal-trigger modal-{{ name }}"
|
|
5
|
+
href="#modal-{{ name }}">{{ modal_title }}</a>
|
|
6
|
+
|
|
7
|
+
<div id="modal-{{ name }}" class="modal left-align">
|
|
8
|
+
<form name="seller" action="{{ action }}" method="post" class="col s12" enctype="multipart/form-data">
|
|
9
|
+
<div class="modal-content">
|
|
10
|
+
<div class="row">
|
|
11
|
+
{% if modal_title %}<h2 class="light" style="margin-top: 0;">{{ modal_title }}</h2>{% endif %}
|
|
12
|
+
<div class="col s12">
|
|
13
|
+
{% if is_pro and user_can_manage_members %}
|
|
14
|
+
{% csrf_token %}
|
|
15
|
+
<p class="flow-text-small grey-text text-darken-2">To add a user, ensure they have already
|
|
16
|
+
created an IndaBOM account and are not currently part of another organization.
|
|
17
|
+
Then, enter their <b>username</b> (not email) and select their role below.</p>
|
|
18
|
+
{{ form|materializecss:'m6 s12' }}
|
|
19
|
+
{% elif is_pro and not user_can_manage_members %}
|
|
20
|
+
<p class="flow-text-small red-text text-darken-1">
|
|
21
|
+
<b>Access Restricted:</b> Please contact an organization administrator to add new members to
|
|
22
|
+
your team.
|
|
23
|
+
</p>
|
|
24
|
+
{% else %}
|
|
25
|
+
<p>Upgrade your organization subscription to professional to manage members.</p>
|
|
26
|
+
{% endif %}
|
|
27
|
+
</div>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div class="modal-footer">
|
|
31
|
+
<a href="#!" class="modal-close waves-effect waves-green btn-flat">Cancel</a>
|
|
32
|
+
{% if is_pro and user_can_manage_members %}
|
|
33
|
+
<button class="waves-effect waves-light btn btn-primary" type="submit" name="{{ name }}">Submit</button>
|
|
34
|
+
{% elif is_pro and not user_can_manage_members %}
|
|
35
|
+
<a href="#!" class="modal-close waves-effect green lighten-1 waves-green btn-flat">Ok</a>
|
|
36
|
+
{% else %}
|
|
37
|
+
<a href="#!" class="modal-close waves-effect green lighten-1 waves-green btn-flat">Ok</a>
|
|
38
|
+
{% endif %}
|
|
39
|
+
</div>
|
|
40
|
+
</form>
|
|
41
|
+
</div>
|
|
42
|
+
|
|
43
|
+
{% block script %}
|
|
44
|
+
<script>
|
|
45
|
+
$(document).ready(function () {
|
|
46
|
+
$('.modal').modal();
|
|
47
|
+
});
|
|
48
|
+
</script>
|
|
49
|
+
{% endblock %}
|
bom/templates/bom/settings.html
CHANGED
|
@@ -207,7 +207,6 @@
|
|
|
207
207
|
<div class="row">
|
|
208
208
|
<div class="col s12">
|
|
209
209
|
<h3>Users</h3>
|
|
210
|
-
{% if users_in_organization.count > 0 %}
|
|
211
210
|
<form name="seller" action="{% url 'bom:settings' tab_anchor=ORGANIZATION_TAB %}" method="post" class="col s12" enctype="multipart/form-data">
|
|
212
211
|
{% csrf_token %}
|
|
213
212
|
<table>
|
|
@@ -237,6 +236,13 @@
|
|
|
237
236
|
class="material-icons left">edit</i>Edit</a>
|
|
238
237
|
</td>
|
|
239
238
|
</tr>
|
|
239
|
+
{% empty %}
|
|
240
|
+
<tr>
|
|
241
|
+
<td colspan="5" style="font-style: italic;">There are no additional
|
|
242
|
+
users in this
|
|
243
|
+
organization.
|
|
244
|
+
</td>
|
|
245
|
+
</tr>
|
|
240
246
|
{% endfor %}
|
|
241
247
|
</tbody>
|
|
242
248
|
</table>
|
|
@@ -247,26 +253,10 @@
|
|
|
247
253
|
</button>
|
|
248
254
|
</div>
|
|
249
255
|
<div class="col s6 right-align">
|
|
250
|
-
{% include 'bom/bom-
|
|
256
|
+
{% include 'bom/bom-modal-add-users.html' with modal_title='Add User' form=user_add_form action=user_add_form_action name='submit-add-user' %}
|
|
251
257
|
</div>
|
|
252
258
|
</div>
|
|
253
259
|
</form>
|
|
254
|
-
{% else %}
|
|
255
|
-
<div class="row" style="padding-left: .75rem;">
|
|
256
|
-
<div class="col s12">
|
|
257
|
-
<p>There are no additional users in this organization: {{ organization }}.</p>
|
|
258
|
-
{% if organization.subscription == 'F' %}
|
|
259
|
-
{% include 'bom/bom-form-modal.html' with modal_title='Add User' name='submit-add-user-free' modal_description='Adding users to your organization is a paid feature, but is free for a limited time while we are still developing the tool. Contact <a href="mailto:info@indabom.com">info@indabom.com</a> if you are interested.' %}
|
|
260
|
-
{% else %}
|
|
261
|
-
{% if organization.subscription_quantity == 0 or users_in_organization.count < organization.subscription_quantity %}
|
|
262
|
-
{% include 'bom/bom-form-modal.html' with modal_title='Add User' form=user_add_form action=user_add_form_action name='submit-add-user' modal_description='Add a user to your organization.' %}
|
|
263
|
-
{% else %}
|
|
264
|
-
<p>You've added the maximum number of users to this organization. Update your subscription to add more.</p>
|
|
265
|
-
{% endif %}
|
|
266
|
-
{% endif %}
|
|
267
|
-
</div>
|
|
268
|
-
</div>
|
|
269
|
-
{% endif %}
|
|
270
260
|
</div>
|
|
271
261
|
</div>
|
|
272
262
|
<div class="row">
|
bom/views/views.py
CHANGED
|
@@ -20,7 +20,6 @@ from django.urls import reverse
|
|
|
20
20
|
from django.utils.encoding import smart_str
|
|
21
21
|
from django.utils.text import smart_split
|
|
22
22
|
from django.views.generic.base import TemplateView
|
|
23
|
-
|
|
24
23
|
from social_django.models import UserSocialAuth
|
|
25
24
|
|
|
26
25
|
import bom.constants as constants
|
|
@@ -74,7 +73,6 @@ from bom.models import (
|
|
|
74
73
|
)
|
|
75
74
|
from bom.utils import check_references_for_duplicates, listify_string, prep_for_sorting_nicely
|
|
76
75
|
|
|
77
|
-
|
|
78
76
|
logger = logging.getLogger(__name__)
|
|
79
77
|
BOM_LOGIN_URL = getattr(settings, "BOM_LOGIN_URL", None) or settings.LOGIN_URL
|
|
80
78
|
|
|
@@ -361,6 +359,10 @@ def bom_settings(request, tab_anchor=None):
|
|
|
361
359
|
users_in_organization = User.objects.filter(
|
|
362
360
|
id__in=UserMeta.objects.filter(organization=organization).values_list('user', flat=True)).exclude(id__in=[organization.owner.id]).order_by(
|
|
363
361
|
'first_name', 'last_name', 'email')
|
|
362
|
+
users_in_organization_count = users_in_organization.count()
|
|
363
|
+
has_member_capacity = users_in_organization_count < organization.subscription_quantity
|
|
364
|
+
is_pro = organization.subscription == constants.SUBSCRIPTION_TYPE_PRO
|
|
365
|
+
user_can_manage_members = request.user.has_perm('bom.manage_members', organization)
|
|
364
366
|
google_authentication = UserSocialAuth.objects.filter(user=user).first()
|
|
365
367
|
|
|
366
368
|
organization_parts_count = Part.objects.filter(organization=organization).count()
|
|
@@ -386,8 +388,10 @@ def bom_settings(request, tab_anchor=None):
|
|
|
386
388
|
|
|
387
389
|
elif 'submit-add-user' in request.POST:
|
|
388
390
|
tab_anchor = ORGANIZATION_TAB
|
|
389
|
-
if
|
|
390
|
-
messages.error(request, "
|
|
391
|
+
if not is_pro:
|
|
392
|
+
messages.error(request, "You need a Pro subscription to add users.")
|
|
393
|
+
elif not user_can_manage_members:
|
|
394
|
+
messages.error(request, "You are not allowed to manage users, contact your organization admin.")
|
|
391
395
|
else:
|
|
392
396
|
user_add_form = UserAddForm(request.POST, organization=organization)
|
|
393
397
|
if user_add_form.is_valid():
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
bom/__init__.py,sha256=HuvSMR9cYQcppTZGD0XjUVUBtHWwWMh1yMQzk_2wTS4,41
|
|
2
2
|
bom/admin.py,sha256=4xN38uSIGo54MVoo2MXUbvcfuPSAifDc8g0arrEAW4Q,4093
|
|
3
3
|
bom/apps.py,sha256=TJMUTSX1h2genPwCq6SN6g1fhrrSmjEXGg2zQFa_ryM,147
|
|
4
|
+
bom/auth_backends.py,sha256=8ViZfP01fITPQnPvsTe_RuKx-ur9dhSOQE3aOp9ESV8,1429
|
|
4
5
|
bom/base_classes.py,sha256=CrWD7wlIkwYb90VoGcVOcw2WoQszRrCje-Re5d_UW1Q,1183
|
|
5
6
|
bom/constants.py,sha256=5CFE0uvKTL91w24rqdAB6EMgpIyw0HhfmmktxF4gCgs,4296
|
|
6
7
|
bom/context_processors.py,sha256=OxMVCqxGtRoHR7aJfTs6xANpxJQzBDUIuNTG1f_Xjoo,312
|
|
@@ -10,7 +11,7 @@ bom/form_fields.py,sha256=tLqchl0j8izBCZnm82NMyHpQV6EuGgCjCl5lrnGR2V0,2816
|
|
|
10
11
|
bom/forms.py,sha256=RUwiMKeaQkapBwz6JCi4Kkl4e5DlFAgSEMjytEDzmoE,69302
|
|
11
12
|
bom/helpers.py,sha256=ONsDM0agG9sKJWMjN4IRNlWx2HNF7T0CXM-ts0GRiAY,15031
|
|
12
13
|
bom/local_settings.py,sha256=yE4aupIquCWsFms44qoCrRrlIyM3sqpOkiwyj1WLxI8,820
|
|
13
|
-
bom/models.py,sha256=
|
|
14
|
+
bom/models.py,sha256=poZoct60kAULCs8RlqC3MFtOl84l2ZfZzjb_d3t9z2g,36537
|
|
14
15
|
bom/part_bom.py,sha256=30HYAKAEhtadiM9tk6vgCQnn7gNJeuXbzF5gXvMvKG4,8720
|
|
15
16
|
bom/settings.py,sha256=jUuy7cKuz9gbZ301L0skMFrnU6qKaBpRUf-rHsTTYJM,8387
|
|
16
17
|
bom/tests.py,sha256=ZqcTUYVXeWjAqzKAV6hp6SKTU0_IOTwIEboTujl7N_M,69905
|
|
@@ -135,6 +136,7 @@ bom/templates/bom/bom-base-menu.html,sha256=PeViadG1PKyljvYfPyBFOKgqWhYHBM0tX5Te
|
|
|
135
136
|
bom/templates/bom/bom-base.html,sha256=SvQOyg8ILULjnJKMaYv6Rf4wS8SNXXIjVY2nl5bm7B4,691
|
|
136
137
|
bom/templates/bom/bom-form-modal.html,sha256=U2EWKjOA41jiScKJHXduJJw_hPNGHtYfdiT3yUuwzRY,1389
|
|
137
138
|
bom/templates/bom/bom-form.html,sha256=1oJetm0fJgPAY1zJ2RNv_Pv9MAja2rjz9pO2bR_UA28,1017
|
|
139
|
+
bom/templates/bom/bom-modal-add-users.html,sha256=1lLgj9acgEkYR3AH177D3xj6meWVmq5MwLDyCg5IV5s,2409
|
|
138
140
|
bom/templates/bom/bom-signup.html,sha256=y0z_kQKCDb91ZdexCmUX1NRKH35l-I_N5f2SgO3G2gI,184
|
|
139
141
|
bom/templates/bom/create-part.html,sha256=UjHx6rkPJl-BRt9gKs5zB3s5Y4vrImDTuD25lvABzAI,3610
|
|
140
142
|
bom/templates/bom/dashboard-menu.html,sha256=4MnFSw0x4w7R0CsSwEDFP3FuZVBFxVChIHr58l3rpSk,799
|
|
@@ -157,7 +159,7 @@ bom/templates/bom/part-revision-release.html,sha256=voG7wmYc1Cm3e_H1IasvQcPuyqnn
|
|
|
157
159
|
bom/templates/bom/search-help.html,sha256=Wh_tXBJtz0bznk0F1C7OSdRhMe2qpOs9NMCBb2i0CFI,4398
|
|
158
160
|
bom/templates/bom/seller-info.html,sha256=MACsHMYQXMWfRslXuvh9hD2z28VXzVi0DSy4yg7WQMk,3595
|
|
159
161
|
bom/templates/bom/sellers.html,sha256=6ut7LwRMGUKYB4BRjiSpDBP9BGgqT7nxpNQpUVWDvkw,5412
|
|
160
|
-
bom/templates/bom/settings.html,sha256=
|
|
162
|
+
bom/templates/bom/settings.html,sha256=tS5PCRQLgWASP40qYsP5D4TZBBlC62KNGmymu1iGH7M,25391
|
|
161
163
|
bom/templates/bom/signup.html,sha256=tB_x7q3IufSNXsd9Dfh8fdWpkiWSGH2_Zgw749B1PaU,884
|
|
162
164
|
bom/templates/bom/table_of_contents.html,sha256=7wXWOfmVkk5Itjax5x1PE-g5QjxqmYBr7RW8NgtGRng,1763
|
|
163
165
|
bom/templates/bom/upload-bom.html,sha256=qGlI9HoUNe9H2m5T5TqKWGphaNupz3Y0020h_7GebsU,5230
|
|
@@ -175,9 +177,9 @@ bom/third_party_apis/mouser.py,sha256=q2-p0k2n-LNel_QRlfak0kAXT-9hh59k_Pt51PTG09
|
|
|
175
177
|
bom/third_party_apis/test_apis.py,sha256=2W0jtTisGTmktC7l556pn9-pZYseTQmmQfo6_4uP4Dc,679
|
|
176
178
|
bom/views/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
177
179
|
bom/views/json_views.py,sha256=CaDMxHGnp2182ZV9QZfNkgM7tc_rNmokkelav9rF2dE,2462
|
|
178
|
-
bom/views/views.py,sha256=
|
|
179
|
-
django_bom-1.
|
|
180
|
-
django_bom-1.
|
|
181
|
-
django_bom-1.
|
|
182
|
-
django_bom-1.
|
|
183
|
-
django_bom-1.
|
|
180
|
+
bom/views/views.py,sha256=hqQllo34l9CHrGig-uq44F7vf-vnceqfvC7OlX8K5Og,72110
|
|
181
|
+
django_bom-1.238.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
182
|
+
django_bom-1.238.dist-info/METADATA,sha256=JU7OY9kGkqHdFq0cMu-5aiht9UHwh3-NPWruRtQ8MNA,7558
|
|
183
|
+
django_bom-1.238.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
184
|
+
django_bom-1.238.dist-info/top_level.txt,sha256=6zytg4lnnobI96dO-ZEadPOCslrrFmf4t2Pnv-y8x0Y,4
|
|
185
|
+
django_bom-1.238.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|