django-bom 1.262__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.
Potentially problematic release.
This version of django-bom might be problematic. Click here for more details.
- bom/__init__.py +1 -0
- bom/admin.py +207 -0
- bom/apps.py +8 -0
- bom/auth_backends.py +47 -0
- bom/base_classes.py +31 -0
- bom/constants.py +217 -0
- bom/context_processors.py +9 -0
- bom/csv_headers.py +252 -0
- bom/decorators.py +32 -0
- bom/form_fields.py +59 -0
- bom/forms.py +1328 -0
- bom/helpers.py +367 -0
- bom/local_settings.py +35 -0
- bom/migrations/0001_initial.py +135 -0
- bom/migrations/0002_auto_20180908_2151.py +24 -0
- bom/migrations/0003_sellerpart_data_source.py +18 -0
- bom/migrations/0004_auto_20180911_0011.py +18 -0
- bom/migrations/0005_auto_20181007_1934.py +56 -0
- bom/migrations/0006_auto_20181007_1949.py +41 -0
- bom/migrations/0007_auto_20181009_0256.py +19 -0
- bom/migrations/0008_auto_20181030_0427.py +19 -0
- bom/migrations/0009_subpart_reference.py +18 -0
- bom/migrations/0010_auto_20181202_0733.py +23 -0
- bom/migrations/0011_auto_20181202_2113.py +22 -0
- bom/migrations/0012_partchangehistory.py +30 -0
- bom/migrations/0013_auto_20190222_1631.py +19 -0
- bom/migrations/0014_auto_20190223_2353.py +18 -0
- bom/migrations/0015_auto_20190303_1915.py +136 -0
- bom/migrations/0016_auto_20190405_2308.py +58 -0
- bom/migrations/0017_auto_20190616_1912.py +19 -0
- bom/migrations/0018_auto_20190616_2143.py +24 -0
- bom/migrations/0019_auto_20190624_1246.py +45 -0
- bom/migrations/0020_auto_20190627_0207.py +38 -0
- bom/migrations/0021_auto_20190627_0428.py +23 -0
- bom/migrations/0022_auto_20190811_2140.py +35 -0
- bom/migrations/0023_auto_20191205_2351.py +255 -0
- bom/migrations/0024_auto_20191214_1342.py +89 -0
- bom/migrations/0025_auto_20191221_1907.py +38 -0
- bom/migrations/0026_auto_20191222_2258.py +22 -0
- bom/migrations/0027_auto_20191222_2347.py +17 -0
- bom/migrations/0028_partrevision_displayable_synopsis.py +74 -0
- bom/migrations/0029_auto_20191231_1630.py +23 -0
- bom/migrations/0030_auto_20200101_2253.py +22 -0
- bom/migrations/0031_auto_20200104_1352.py +38 -0
- bom/migrations/0032_auto_20200126_1806.py +27 -0
- bom/migrations/0033_auto_20200203_0618.py +29 -0
- bom/migrations/0034_auto_20200222_0359.py +30 -0
- bom/migrations/0035_auto_20200303_0111.py +34 -0
- bom/migrations/0036_auto_20200303_0538.py +17 -0
- bom/migrations/0037_auto_20200405_1642.py +44 -0
- bom/migrations/0038_auto_20200422_0504.py +19 -0
- bom/migrations/0039_auto_20200929_2315.py +41 -0
- bom/migrations/0040_alter_organization_currency.py +19 -0
- bom/migrations/0041_organization_subscription_quantity.py +18 -0
- bom/migrations/0042_auto_20210720_2137.py +23 -0
- bom/migrations/0043_auto_20211123_0157.py +24 -0
- bom/migrations/0044_auto_20220831_1241.py +23 -0
- bom/migrations/0045_sellerpart_link.py +18 -0
- bom/migrations/0046_alter_sellerpart_unique_together.py +17 -0
- bom/migrations/0047_sellerpart_seller_part_number.py +18 -0
- bom/migrations/0048_rename_part_organization_number_class_bom_part_organiz_b333d6_idx_and_more.py +1017 -0
- bom/migrations/0049_alter_assembly_id_alter_assemblysubparts_id_and_more.py +99 -0
- bom/migrations/0050_alter_organization_options.py +17 -0
- bom/migrations/0051_alter_manufacturer_organization_and_more.py +41 -0
- bom/migrations/0052_remove_partrevision_attribute_and_more.py +584 -0
- bom/migrations/__init__.py +0 -0
- bom/models.py +886 -0
- bom/part_bom.py +192 -0
- bom/settings.py +262 -0
- bom/static/bom/css/dashboard.css +17 -0
- bom/static/bom/css/jquery.treetable.css +28 -0
- bom/static/bom/css/materialize.min.css +13 -0
- bom/static/bom/css/part-info.css +15 -0
- bom/static/bom/css/style.css +482 -0
- bom/static/bom/css/tablesorter-theme.materialize.css +176 -0
- bom/static/bom/css/treetable-theme.css +42 -0
- bom/static/bom/doc/sample_part_classes.csv +38 -0
- bom/static/bom/doc/test_bom.csv +6 -0
- bom/static/bom/doc/test_bom_5_intelligent.csv +4 -0
- bom/static/bom/doc/test_full_bom.csv +37 -0
- bom/static/bom/doc/test_new_parts.csv +5 -0
- bom/static/bom/doc/test_new_parts_5_intelligent.csv +5 -0
- bom/static/bom/img/_ionicons_svg_md-arrow-dropdown.svg +1 -0
- bom/static/bom/img/_ionicons_svg_md-arrow-dropright.svg +1 -0
- bom/static/bom/img/favicon.ico +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_dark_disabled_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_dark_focus_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_dark_normal_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_dark_pressed_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_light_disabled_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_light_focus_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_light_normal_web.png +0 -0
- bom/static/bom/img/google/web/1x/btn_google_signin_light_pressed_web.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_dark_disabled_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_dark_focus_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_dark_normal_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_dark_pressed_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_light_disabled_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_light_focus_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_light_normal_web@2x.png +0 -0
- bom/static/bom/img/google/web/2x/btn_google_signin_light_pressed_web@2x.png +0 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_disabled_ios.eps +814 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_disabled_ios.svg +24 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_focus_ios.eps +1866 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_focus_ios.svg +51 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_normal_ios.eps +1031 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_normal_ios.svg +50 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_pressed_ios.eps +1031 -0
- bom/static/bom/img/google/web/vector/btn_google_dark_pressed_ios.svg +50 -0
- bom/static/bom/img/google/web/vector/btn_google_light_disabled_ios.eps +814 -0
- bom/static/bom/img/google/web/vector/btn_google_light_disabled_ios.svg +24 -0
- bom/static/bom/img/google/web/vector/btn_google_light_focus_ios.eps +1837 -0
- bom/static/bom/img/google/web/vector/btn_google_light_focus_ios.svg +44 -0
- bom/static/bom/img/google/web/vector/btn_google_light_normal_ios.eps +1002 -0
- bom/static/bom/img/google/web/vector/btn_google_light_normal_ios.svg +43 -0
- bom/static/bom/img/google/web/vector/btn_google_light_pressed_ios.eps +1002 -0
- bom/static/bom/img/google/web/vector/btn_google_light_pressed_ios.svg +43 -0
- bom/static/bom/img/google_drive_logo.svg +1 -0
- bom/static/bom/img/indabom.png +0 -0
- bom/static/bom/img/mouser.png +0 -0
- bom/static/bom/img/octopart_blue.svg +19 -0
- bom/static/bom/js/formset-handler.js +65 -0
- bom/static/bom/js/jquery-3.4.1.min.js +2 -0
- bom/static/bom/js/jquery.ba-floatingscrollbar.min.js +10 -0
- bom/static/bom/js/jquery.treetable.js +629 -0
- bom/static/bom/js/materialize.min.js +6 -0
- bom/templates/bom/account-delete.html +23 -0
- bom/templates/bom/add-manufacturer-part.html +66 -0
- bom/templates/bom/add-sellerpart.html +93 -0
- bom/templates/bom/base-menu.html +16 -0
- bom/templates/bom/base.html +129 -0
- bom/templates/bom/bom-action-btn.html +23 -0
- bom/templates/bom/bom-action-table.html +57 -0
- bom/templates/bom/bom-base-menu.html +6 -0
- bom/templates/bom/bom-base.html +24 -0
- bom/templates/bom/bom-form-modal.html +36 -0
- bom/templates/bom/bom-form.html +30 -0
- bom/templates/bom/bom-modal-add-users.html +49 -0
- bom/templates/bom/bom-signup.html +12 -0
- bom/templates/bom/components/bom-flat.html +131 -0
- bom/templates/bom/components/bom-indented.html +237 -0
- bom/templates/bom/components/manufacturer-part-list.html +270 -0
- bom/templates/bom/components/seller-part-list.html +62 -0
- bom/templates/bom/create-part.html +65 -0
- bom/templates/bom/dashboard-menu.html +15 -0
- bom/templates/bom/dashboard.html +303 -0
- bom/templates/bom/edit-manufacturer-part.html +72 -0
- bom/templates/bom/edit-part-class.html +120 -0
- bom/templates/bom/edit-part.html +67 -0
- bom/templates/bom/edit-quantity-of-measure.html +119 -0
- bom/templates/bom/edit-user-meta.html +70 -0
- bom/templates/bom/help.html +1356 -0
- bom/templates/bom/manufacturer-info.html +82 -0
- bom/templates/bom/manufacturers.html +97 -0
- bom/templates/bom/nothing-to-see.html +15 -0
- bom/templates/bom/organization-create.html +135 -0
- bom/templates/bom/part-info.html +448 -0
- bom/templates/bom/part-revision-display.html +50 -0
- bom/templates/bom/part-revision-edit.html +39 -0
- bom/templates/bom/part-revision-manage-bom.html +115 -0
- bom/templates/bom/part-revision-new.html +57 -0
- bom/templates/bom/part-revision-release.html +41 -0
- bom/templates/bom/search-help.html +101 -0
- bom/templates/bom/seller-info.html +82 -0
- bom/templates/bom/sellers.html +97 -0
- bom/templates/bom/settings.html +734 -0
- bom/templates/bom/signup.html +28 -0
- bom/templates/bom/subscription_panel.html +16 -0
- bom/templates/bom/table_of_contents.html +47 -0
- bom/templates/bom/upload-bom.html +111 -0
- bom/templates/bom/upload-parts-help.html +103 -0
- bom/templates/bom/upload-parts.html +50 -0
- bom/templates/registration/login.html +39 -0
- bom/tests.py +1592 -0
- bom/third_party_apis/__init__.py +0 -0
- bom/third_party_apis/base_api.py +51 -0
- bom/third_party_apis/google_drive.py +166 -0
- bom/third_party_apis/mouser.py +132 -0
- bom/third_party_apis/test_apis.py +24 -0
- bom/urls.py +100 -0
- bom/utils.py +228 -0
- bom/validators.py +23 -0
- bom/views/__init__.py +0 -0
- bom/views/json_views.py +55 -0
- bom/views/views.py +1773 -0
- bom/wsgi.py +16 -0
- django_bom-1.262.dist-info/METADATA +206 -0
- django_bom-1.262.dist-info/RECORD +191 -0
- django_bom-1.262.dist-info/WHEEL +5 -0
- django_bom-1.262.dist-info/licenses/LICENSE +674 -0
- django_bom-1.262.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
{# To include this widget, make sure to pass: `parts`, `order_by`, `part`, `part_revision`, `profile` variables - TODO Could remove part if we pass part revision #}
|
|
2
|
+
{% load static %}
|
|
3
|
+
|
|
4
|
+
{% block head %}
|
|
5
|
+
<link rel="stylesheet" type="text/css" href="{% static 'bom/css/treetable-theme.css' %}"/>
|
|
6
|
+
<link rel="stylesheet" type="text/css" href="{% static 'bom/css/jquery.treetable.css' %}"/>
|
|
7
|
+
<link rel="stylesheet" type="text/css" href="{% static 'bom/css/tablesorter-theme.materialize.css' %}"/>
|
|
8
|
+
{% endblock %}
|
|
9
|
+
|
|
10
|
+
<div class="row printer-hide" style="padding-top: 16px;">
|
|
11
|
+
<div class="col s6">
|
|
12
|
+
<a class="waves-effect waves-green btn-flat btn-icon-round button-expand tooltipped" data-position="bottom" data-tooltip="Expand BOM" onclick="expandTree()"><i class="material-icons">unfold_more</i></a>
|
|
13
|
+
<a class="waves-effect waves-green btn-flat btn-icon-round button-collapse tooltipped" data-position="bottom" data-tooltip="Collapse BOM" onclick="collapseTree()"><i class="material-icons">unfold_less</i></a>
|
|
14
|
+
<a class="waves-effect waves-green btn-flat btn-icon-round button-reset-filter-indented tooltipped" data-position="bottom" data-tooltip="Reset filter" onclick="refreshTableSorterIndented()"><i
|
|
15
|
+
class="material-icons">refresh</i></a>
|
|
16
|
+
</div>
|
|
17
|
+
<div class="col s6 right-align">
|
|
18
|
+
{% if manage and profile.role == 'A' %}
|
|
19
|
+
<a class="btn waves-effect waves-green btn-flat red-text"
|
|
20
|
+
href="{% url 'bom:part-remove-all-subparts' part_id=part.id part_revision_id=part_revision_id %}"
|
|
21
|
+
onclick="return confirm('Are you sure you want to remove ALL subparts from {{ part.full_part_number }}?')">Remove All Subparts</a>
|
|
22
|
+
{% else %}
|
|
23
|
+
<!-- Dropdown Trigger -->
|
|
24
|
+
<button class="dropdown-trigger-indented waves-effect waves-green btn-flat"
|
|
25
|
+
data-target='manage-bom-{{ part.id }}-indented'>
|
|
26
|
+
<i class="material-icons right">arrow_drop_down</i>Manage
|
|
27
|
+
</button>
|
|
28
|
+
<!-- Dropdown Structure -->
|
|
29
|
+
<ul id='manage-bom-{{ part.id }}-indented' class='dropdown-content'>
|
|
30
|
+
{% if profile.role == 'A' %}
|
|
31
|
+
<li>{% if part_revision.configuration == 'W' %}
|
|
32
|
+
<a class="green-text text-lighten-1" href="{% url 'bom:part-manage-bom' part_id=part.id part_revision_id=part_revision.id %}">
|
|
33
|
+
<i class="material-icons green-text text-lighten-1">edit</i>Edit BOM</a>
|
|
34
|
+
{% else %}
|
|
35
|
+
<a class="grey-text text-lighten-1" onclick="alert('Can\'t edit a released part!')"><i class="material-icons grey-text text-lighten-1">edit</i>Edit BOM</a>
|
|
36
|
+
{% endif %}
|
|
37
|
+
</li>
|
|
38
|
+
{% endif %}
|
|
39
|
+
{% if part_revision %}
|
|
40
|
+
<li><a class="green-text text-lighten-1" href="{% url 'bom:part-revision-export-bom' part_revision_id=part_revision.id %}">
|
|
41
|
+
<i class="material-icons green-text text-lighten-1">cloud_download</i>Download CSV</a>
|
|
42
|
+
</li>
|
|
43
|
+
<li><a class="green-text text-lighten-1" href="{% url 'bom:part-revision-export-bom-sourcing' part_revision_id=part_revision.id %}">
|
|
44
|
+
<i class="material-icons green-text text-lighten-1">cloud_download</i>Download CSV (sourcing)</a>
|
|
45
|
+
</li>
|
|
46
|
+
<li><a class="green-text text-lighten-1" href="{% url 'bom:part-revision-export-bom-sourcing-detailed' part_revision_id=part_revision.id %}">
|
|
47
|
+
<i class="material-icons green-text text-lighten-1">cloud_download</i>Download CSV (sourcing detailed)</a>
|
|
48
|
+
</li>
|
|
49
|
+
{% else %}
|
|
50
|
+
<li><a class="green-text text-lighten-1 disabled" href=""><i class="material-icons green-text text-lighten-1">cloud_download</i>Download CSV</a></li>
|
|
51
|
+
{% endif %}
|
|
52
|
+
</ul>
|
|
53
|
+
{% endif %}
|
|
54
|
+
</div>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="responsive-table-wrapper">
|
|
57
|
+
<table id="indented-bom" class="highlight part-list tight single-line">
|
|
58
|
+
<thead>
|
|
59
|
+
<tr>
|
|
60
|
+
<th class="text-normal">Level</th>
|
|
61
|
+
<th class="text-normal">Part No.</th>
|
|
62
|
+
<th class="text-normal">Qty</th>
|
|
63
|
+
<th class="text-normal">Reference</th>
|
|
64
|
+
<th class="text-normal">DNL</th>
|
|
65
|
+
<th class="text-normal">Synopsis</th>
|
|
66
|
+
<th class="text-normal">Rev</th>
|
|
67
|
+
<th class="text-normal">Manufacturer</th>
|
|
68
|
+
<th class="text-normal">MPN</th>
|
|
69
|
+
<!-- <th class="text-normal"><i>Ext. Qty</i></th> -->
|
|
70
|
+
<!-- <th class="text-normal"><i>Order Qty</i></th> -->
|
|
71
|
+
<th class="text-normal">Seller</th>
|
|
72
|
+
<th class="text-normal">Price</th>
|
|
73
|
+
<!-- <th class="text-normal"><i>Ext Cost</i></th> -->
|
|
74
|
+
<th class="text-normal">NRE</th>
|
|
75
|
+
{% if profile.role == 'A' and manage %}
|
|
76
|
+
<th class="sorter-false" data-sorter="false">Manage</th>
|
|
77
|
+
{% endif %}
|
|
78
|
+
</tr>
|
|
79
|
+
</thead>
|
|
80
|
+
{% for bom_id, subpart in bom_items.items %}
|
|
81
|
+
{% if subpart.references|length > 25 %}
|
|
82
|
+
<div id="modal-{{ subpart.part.id }}-references" class="modal">
|
|
83
|
+
<div class="modal-content">
|
|
84
|
+
<h2>{{ subpart.part.full_part_number }} References</h2>
|
|
85
|
+
<p>{{ subpart.references }}</p>
|
|
86
|
+
</div>
|
|
87
|
+
<div class="modal-footer">
|
|
88
|
+
<a href="#!" class="modal-close waves-effect green lighten-1 waves-green btn-flat">Ok</a>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
{% endif %}
|
|
92
|
+
{% if subpart.part_revision.synopsis|length > 75 %}
|
|
93
|
+
<div id="modal-{{ subpart.part.id }}-synposis" class="modal">
|
|
94
|
+
<div class="modal-content">
|
|
95
|
+
<h2>{{ subpart.part.full_part_number }} Synposis</h2>
|
|
96
|
+
<p>{{ subpart.part_revision.synopsis }}</p>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="modal-footer">
|
|
99
|
+
<a href="#!" class="modal-close waves-effect green lighten-1 waves-green btn-flat">Ok</a>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
{% endif %}
|
|
103
|
+
<tr data-tt-id="{{ bom_id }}"
|
|
104
|
+
{% if subpart.parent_id %}data-tt-parent-id="{{ subpart.parent_id }}"{% endif %}>
|
|
105
|
+
<td class="text-normal">{{ subpart.indent_level }}</td>
|
|
106
|
+
<td class="text-normal"><a href="{% url 'bom:part-info' part_id=subpart.part.id %}">{{ subpart.part.full_part_number }}</a></td>
|
|
107
|
+
<td class="text-normal">{{ subpart.quantity | floatformat }}</td>
|
|
108
|
+
<td class="text-normal">{{ subpart.references | default:"-" | truncatechars:25 }}{% if subpart.references|length > 25 %}
|
|
109
|
+
<a class="modal-trigger modal-{{ subpart.part.id }}-references" href="#modal-{{ subpart.part.id }}-references"> [+]</a>
|
|
110
|
+
{% endif %}</td>
|
|
111
|
+
<td class="text-normal">{{ subpart.do_not_load | default:"-" }}</td>
|
|
112
|
+
<td class="text-normal">{{ subpart.part_revision.synopsis | truncatechars:75 }}{% if subpart.part_revision.synopsis|length > 75 %}
|
|
113
|
+
<a class="modal-trigger modal-{{ subpart.part.id }}-synposis" href="#modal-{{ subpart.part.id }}-synposis"> [+]</a>
|
|
114
|
+
{% endif %}
|
|
115
|
+
<td class="text-normal">{{ subpart.part_revision.revision }}</td>
|
|
116
|
+
<td class="text-normal">{{ subpart.part.primary_manufacturer_part.manufacturer.name }}
|
|
117
|
+
{% if subpart.part.manufacturer_parts.count > 1 %}
|
|
118
|
+
<i>(+{{ subpart.part.manufacturer_parts.count }})</i>{% endif %}</td>
|
|
119
|
+
<td class="text-normal">{% if subpart.part.primary_manufacturer_part.link %}<a href="{{ subpart.part.primary_manufacturer_part.link }}" target="_blank">{% endif %}{{ subpart.part.primary_manufacturer_part.manufacturer_part_number }}{% if subpart.part.primary_manufacturer_part.link %} <i class="material-icons" style="font-size: 14px;">open_in_new</i></a>{% endif %}</td>
|
|
120
|
+
<td class="text-normal" id="bom-indented-seller-name-{{ subpart.part_revision.id }}">{{ subpart.seller_part.seller.name | default:"-" }}</td>
|
|
121
|
+
<td class="text-normal" id="bom-indented-unit-cost-{{ subpart.part_revision.id }}">{{ subpart.seller_part.unit_cost | default:"-" }}</td>
|
|
122
|
+
<td class="text-normal">{{ subpart.seller_part.nre_cost | default:"-" }}</td>
|
|
123
|
+
{% if subpart.indent_level == 1 and subpart.subpart is not None and manage %}
|
|
124
|
+
<td>
|
|
125
|
+
<a class='dropdown-trigger' href='#'
|
|
126
|
+
data-target='dropdown{{ subpart.subpart.id }}'><i class="material-icons left green-text">more_horiz</i></a>
|
|
127
|
+
<!-- Dropdown Structure -->
|
|
128
|
+
<ul id='dropdown{{ subpart.subpart.id }}' class='dropdown-content'>
|
|
129
|
+
<li><a class="green-text text-lighten-1"
|
|
130
|
+
href="{% url 'bom:part-edit-subpart' part_id=part.id part_revision_id=part_revision_id subpart_id=subpart.subpart.id %}"><i
|
|
131
|
+
class="material-icons green-text text-lighten-1">edit</i>Edit or Change
|
|
132
|
+
Rev</a>
|
|
133
|
+
<li class="divider" tabindex="-1"></li>
|
|
134
|
+
<li><a class="red-text text-lighten-1"
|
|
135
|
+
href="{% url 'bom:part-remove-subpart' part_id=part.id part_revision_id=part_revision.id subpart_id=subpart.subpart.id %}"
|
|
136
|
+
onclick="return confirm('Are you sure you want to remove {{ subpart.part.full_part_number }}?')"><i
|
|
137
|
+
class="material-icons red-text text-lighten-1">delete_forever</i>Remove</a>
|
|
138
|
+
</li>
|
|
139
|
+
</ul>
|
|
140
|
+
</td>
|
|
141
|
+
{% elif manage %}
|
|
142
|
+
<td></td>
|
|
143
|
+
{% endif %}
|
|
144
|
+
</tr>
|
|
145
|
+
{% empty %}
|
|
146
|
+
<tr>
|
|
147
|
+
<td colspan="99" style="text-align: center; height: 20vh;"><i>This part does not contain any other parts.</i></td>
|
|
148
|
+
</tr>
|
|
149
|
+
{% endfor %}
|
|
150
|
+
</table>
|
|
151
|
+
</div>
|
|
152
|
+
|
|
153
|
+
<!-- Tree Table -->
|
|
154
|
+
<script src="{% static 'bom/js/jquery.treetable.js' %}"></script>
|
|
155
|
+
<script>
|
|
156
|
+
$("#indented-bom").treetable({
|
|
157
|
+
expandable: true,
|
|
158
|
+
indent: 2,
|
|
159
|
+
initialState: 'collapsed',
|
|
160
|
+
onInitialized: function () {
|
|
161
|
+
$("#indented-bom").treetable("reveal", {{ part_revision.id }});
|
|
162
|
+
$('.button-collapse').hide();
|
|
163
|
+
},
|
|
164
|
+
onNodeCollapse: function () {
|
|
165
|
+
$('.button-expand').show();
|
|
166
|
+
},
|
|
167
|
+
onNodeExpand: function () {
|
|
168
|
+
$('.button-collapse').show();
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
function expandTree() {
|
|
173
|
+
$('#indented-bom').treetable('expandAll');
|
|
174
|
+
$('.button-expand').hide();
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function collapseTree() {
|
|
179
|
+
$('#indented-bom').treetable('collapseAll');
|
|
180
|
+
$('#indented-bom').treetable("reveal", {{ part_revision.id }});
|
|
181
|
+
$('.button-collapse').hide();
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
</script>
|
|
185
|
+
|
|
186
|
+
<!-- Table Sorter -->
|
|
187
|
+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.31.2/js/jquery.tablesorter.min.js"></script>
|
|
188
|
+
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.31.2/js/jquery.tablesorter.widgets.min.js"></script>
|
|
189
|
+
<script>
|
|
190
|
+
$(function () {
|
|
191
|
+
$("#indented-bom").on('update', initTableSorterIndented);
|
|
192
|
+
initTableSorterIndented();
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
function initTableSorterIndented() {
|
|
196
|
+
$('.button-reset-filter-indented').hide();
|
|
197
|
+
$("#indented-bom").tablesorter({
|
|
198
|
+
theme: "materialize",
|
|
199
|
+
headers: {
|
|
200
|
+
'.manage': {
|
|
201
|
+
sorter: false
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}).bind("sortEnd", function (e, t) {
|
|
205
|
+
$('.button-reset-filter-indented').show();
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function refreshTableSorterIndented() {
|
|
210
|
+
$('#indented-bom').trigger('sortReset');
|
|
211
|
+
$('.button-reset-filter-indented').hide();
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
</script>
|
|
215
|
+
|
|
216
|
+
<script>
|
|
217
|
+
$(document).ready(function () {
|
|
218
|
+
$('.dropdown-trigger-indented').dropdown({
|
|
219
|
+
inDuration: 200,
|
|
220
|
+
outDuration: 225,
|
|
221
|
+
constrainWidth: false, // Does not change width of dropdown to that of the activator
|
|
222
|
+
hover: false, // Activate on hover
|
|
223
|
+
gutter: 0, // Spacing from edge
|
|
224
|
+
alignment: 'left', // Displays dropdown with edge aligned to the left of button
|
|
225
|
+
stopPropagation: false, // Stops event propagation
|
|
226
|
+
coverTrigger: false
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
$('.tooltipped').tooltip();
|
|
230
|
+
});
|
|
231
|
+
</script>
|
|
232
|
+
|
|
233
|
+
<script>
|
|
234
|
+
$(document).ready(function () {
|
|
235
|
+
$('.modal').modal();
|
|
236
|
+
});
|
|
237
|
+
</script>
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
{# To include this widget, make sure to pass: `manufacturer_parts`, `profile`, (optional, for mouser) `part_revision` variables #}
|
|
2
|
+
{% load static %}
|
|
3
|
+
|
|
4
|
+
{% block head %}
|
|
5
|
+
<link rel="stylesheet" type="text/css" href="{% static 'bom/css/treetable-theme.css' %}"/>
|
|
6
|
+
<link rel="stylesheet" type="text/css" href="{% static 'bom/css/jquery.treetable.css' %}"/>
|
|
7
|
+
{% endblock %}
|
|
8
|
+
|
|
9
|
+
<div class="responsive-table-wrapper">
|
|
10
|
+
<table id="manufacturer-parts" class="highlight tight treetable">
|
|
11
|
+
<thead>
|
|
12
|
+
<tr>
|
|
13
|
+
<th>MPN</th>
|
|
14
|
+
<th>Manufacturer</th>
|
|
15
|
+
{% if show_part %}<th>Part</th>{% endif %}
|
|
16
|
+
<th>Seller</th>
|
|
17
|
+
<th>PN</th>
|
|
18
|
+
<th>MOQ</th>
|
|
19
|
+
<th>MPQ</th>
|
|
20
|
+
<th>Price</th>
|
|
21
|
+
<th>Lead Time (days)</th>
|
|
22
|
+
<th>NRE</th>
|
|
23
|
+
<th>NCNR</th>
|
|
24
|
+
<th colspan="2">Manage</th>
|
|
25
|
+
</tr>
|
|
26
|
+
</thead>
|
|
27
|
+
<tbody>
|
|
28
|
+
{% for manufacturer_part in manufacturer_parts %}
|
|
29
|
+
<tr id="sourcing-mp-{{ manufacturer_part.id }}" data-tt-id="{{ manufacturer_part.id }}">
|
|
30
|
+
<td>{% if manufacturer_part.link %}<a href="{{ manufacturer_part.link }}" target="_blank">{% endif %}{{ manufacturer_part.manufacturer_part_number }}{% if manufacturer_part.link %} <i class="material-icons" style="font-size: 14px;">open_in_new</i></a>{% endif %}</td>
|
|
31
|
+
<td>{% if manufacturer_part.manufacturer %}<a href="{% url 'bom:manufacturer-info' manufacturer_id=manufacturer_part.manufacturer_id %}">{% endif %}{{ manufacturer_part.manufacturer }}{% if manufacturer_part.manufacturer %}</a>{% endif %}</td>
|
|
32
|
+
{% if show_part %}<td><a href="{% url 'bom:part-info' part_id=manufacturer_part.part_id %}">{{ manufacturer_part.part }}</a></td>{% endif %}
|
|
33
|
+
<td id="sourcing-mp-{{ manufacturer_part.id }}-name">{% if manufacturer_part.optimal_seller.link %}<a href="{{ manufacturer_part.optimal_seller.link }}" target="_blank">{% endif %}{{ manufacturer_part.optimal_seller.seller.name }}{% if manufacturer_part.optimal_seller.link %} <i class="material-icons" style="font-size: 14px;">open_in_new</i></a>{% endif %}</td>
|
|
34
|
+
<td id="sourcing-mp-{{ manufacturer_part.id }}-pn">{{ manufacturer_part.optimal_seller.seller_part_number }}</td>
|
|
35
|
+
<td id="sourcing-mp-{{ manufacturer_part.id }}-moq">{{ manufacturer_part.optimal_seller.minimum_order_quantity }}</td>
|
|
36
|
+
<td id="sourcing-mp-{{ manufacturer_part.id }}-mpq">{{ manufacturer_part.optimal_seller.minimum_pack_quantity }}</td>
|
|
37
|
+
<td id="sourcing-mp-{{ manufacturer_part.id }}-cost">{{ manufacturer_part.optimal_seller.unit_cost }}</td>
|
|
38
|
+
<td id="sourcing-mp-{{ manufacturer_part.id }}-lt">{{ manufacturer_part.optimal_seller.lead_time_days }}</td>
|
|
39
|
+
<td id="sourcing-mp-{{ manufacturer_part.id }}-nre">{{ manufacturer_part.optimal_seller.nre_cost }}</td>
|
|
40
|
+
<td id="sourcing-mp-{{ manufacturer_part.id }}-ncnr">{{ manufacturer_part.optimal_seller.ncnr }}</td>
|
|
41
|
+
{# <td style="min-width: 0px;"></td>#}
|
|
42
|
+
<td>
|
|
43
|
+
{% if profile.role == 'A' %}
|
|
44
|
+
<!-- Dropdown Trigger -->
|
|
45
|
+
<a class='dropdown-trigger' href='#' data-target='dropdown{{ manufacturer_part.id }}'><i
|
|
46
|
+
class="material-icons right green-text lighten-1">more_horiz</i></a>
|
|
47
|
+
<!-- Dropdown Structure -->
|
|
48
|
+
<ul id='dropdown{{ manufacturer_part.id }}' class='dropdown-content'>
|
|
49
|
+
<li><a class="green-text text-lighten-1"
|
|
50
|
+
href="{% url 'bom:manufacturer-part-edit' manufacturer_part_id=manufacturer_part.id %}"><i
|
|
51
|
+
class="material-icons green-text text-lighten-1">edit</i>edit</a></li>
|
|
52
|
+
<li><a class="green-text text-lighten-1"
|
|
53
|
+
href="{% url 'bom:manufacturer-part-add-sellerpart' manufacturer_part_id=manufacturer_part.id %}"><i
|
|
54
|
+
class="material-icons green-text text-lighten-1">add</i>add seller part</a>
|
|
55
|
+
</li>
|
|
56
|
+
<li class="divider" tabindex="-1"></li>
|
|
57
|
+
<li><a class="red-text text-lighten-1"
|
|
58
|
+
href="{% url 'bom:manufacturer-part-delete' manufacturer_part_id=manufacturer_part.id %}"><i
|
|
59
|
+
class="material-icons red-text text-lighten-1">delete_forever</i>delete</a>
|
|
60
|
+
</li>
|
|
61
|
+
</ul>
|
|
62
|
+
{% endif %}
|
|
63
|
+
</td>
|
|
64
|
+
</tr>
|
|
65
|
+
{% for seller_part in manufacturer_part.seller_parts %}
|
|
66
|
+
<tr data-tt-id="0" data-tt-parent-id="{{ manufacturer_part.id }}" {% if forloop.last %}id="sourcing-{{ manufacturer_part.id }}-last"{% endif %}>
|
|
67
|
+
<td></td>
|
|
68
|
+
<td></td>
|
|
69
|
+
<td>{% if seller_part.link %}<a href="{{ seller_part.link }}" target="_blank">{% endif %}{{ seller_part.seller.name }}{% if seller_part.link %} <i class="material-icons" style="font-size: 14px;">open_in_new</i></a>{% endif %}</td>
|
|
70
|
+
<td>{{ seller_part.seller_part_number }}</td>
|
|
71
|
+
<td>{{ seller_part.minimum_order_quantity }}</td>
|
|
72
|
+
<td>{{ seller_part.minimum_pack_quantity }}</td>
|
|
73
|
+
<td>{{ seller_part.unit_cost }}</td>
|
|
74
|
+
<td>{{ seller_part.lead_time_days }}</td>
|
|
75
|
+
<td>{{ seller_part.nre_cost }}</td>
|
|
76
|
+
<td>{{ seller_part.ncnr }}</td>
|
|
77
|
+
<td>{% if profile.role == 'A' %}
|
|
78
|
+
<a href="{% url 'bom:sellerpart-edit' sellerpart_id=seller_part.id %}">edit</a> |
|
|
79
|
+
<a href="{% url 'bom:sellerpart-delete' sellerpart_id=seller_part.id %}">delete</a>
|
|
80
|
+
{% endif %}</td>
|
|
81
|
+
</tr>
|
|
82
|
+
{% empty %}
|
|
83
|
+
<tr data-tt-id="0" data-tt-parent-id="{{ manufacturer_part.id }}">
|
|
84
|
+
<td colspan="99" style="text-align: center;"><i>This manufacturer part has no sellers entered yet.</i></td>
|
|
85
|
+
</tr>
|
|
86
|
+
{% endfor %}
|
|
87
|
+
{% empty %}
|
|
88
|
+
<tr>
|
|
89
|
+
<td colspan="99" style="text-align: center; height: 20vh;"><i>None found.</i></td>
|
|
90
|
+
</tr>
|
|
91
|
+
{% endfor %}
|
|
92
|
+
</tbody>
|
|
93
|
+
</table>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<script src="{% static 'bom/js/jquery.treetable.js' %}"></script>
|
|
97
|
+
<script>
|
|
98
|
+
$("#manufacturer-parts").treetable({
|
|
99
|
+
expandable: true,
|
|
100
|
+
indent: 2,
|
|
101
|
+
initialState: 'collapsed',
|
|
102
|
+
});
|
|
103
|
+
</script>
|
|
104
|
+
|
|
105
|
+
<!-- Floating Horizontal Scrollbar -->
|
|
106
|
+
<script type="text/javascript" src="{% static 'bom/js/jquery.ba-floatingscrollbar.min.js' %}"></script>
|
|
107
|
+
<script>
|
|
108
|
+
$(function () {
|
|
109
|
+
$('.responsive-table-wrapper').floatingScrollbar();
|
|
110
|
+
});
|
|
111
|
+
</script>
|
|
112
|
+
|
|
113
|
+
<!-- Match Seller Parts -->
|
|
114
|
+
<script>
|
|
115
|
+
let partMatchData = {};
|
|
116
|
+
const mouserImage = '<img height="18" style="padding: 4px 4px 0 4px;" alt="Mouser" title="Via Mouser.com" src="{% static 'bom/img/mouser.png' %}">';
|
|
117
|
+
let specsUnitCost;
|
|
118
|
+
let specsEstCostPerOrder;
|
|
119
|
+
let specsMissingParts;
|
|
120
|
+
let dataSheet;
|
|
121
|
+
$(document).ready(function () {
|
|
122
|
+
{% if part_revision.id and mouser_parts %}
|
|
123
|
+
$.get("{% url 'json:mouser-part-match-bom' part_revision_id=part_revision.id %}",
|
|
124
|
+
function (response) {
|
|
125
|
+
partMatchData = response['content'];
|
|
126
|
+
const errors = response['errors'];
|
|
127
|
+
if (errors.length > 0) {
|
|
128
|
+
console.error(errors);
|
|
129
|
+
} else {
|
|
130
|
+
const flatBom = partMatchData['flat_bom'];
|
|
131
|
+
const bomParts = flatBom['parts'];
|
|
132
|
+
const mouserInfo = flatBom['parts']['{{ part_revision.id }}']['api_info'];
|
|
133
|
+
const sourcingParts = mouserInfo['seller_parts'];
|
|
134
|
+
const stockParsed = mouserInfo['stock_parsed'];
|
|
135
|
+
const optimalSellerPart = flatBom['parts']['{{ part_revision.id }}']['seller_part'];
|
|
136
|
+
specsUnitCost = parseFloat(flatBom['unit_cost']).toFixed(4);
|
|
137
|
+
specsEstCostPerOrder = parseFloat(flatBom['out_of_pocket_cost']).toFixed(2);
|
|
138
|
+
specsMissingParts = flatBom['missing_item_costs'];
|
|
139
|
+
dataSheet = mouserInfo['data_sheet'];
|
|
140
|
+
mouserStock = mouserInfo['stock'];
|
|
141
|
+
const sellerLink = mouserInfo['product_detail_url'];
|
|
142
|
+
updateSpecs(specsUnitCost, specsEstCostPerOrder, specsMissingParts, dataSheet, mouserStock, sellerLink);
|
|
143
|
+
updateFlat(bomParts);
|
|
144
|
+
updateIndented(bomParts);
|
|
145
|
+
updateSourcing(sourcingParts, optimalSellerPart, stockParsed);
|
|
146
|
+
$("#indented-bom").trigger('update');
|
|
147
|
+
$("#flat-bom").trigger('update');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
{% endif %}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
function updateSourcing(sellerParts, optimalSellerPart, stock) {
|
|
155
|
+
let lastRowDict = {};
|
|
156
|
+
let mouserOptimizations = 0;
|
|
157
|
+
if (sellerParts) {
|
|
158
|
+
sellerParts.forEach((sellerPart, index) => {
|
|
159
|
+
if (sellerPart['data_source'] === 'Mouser') mouserOptimizations += 1;
|
|
160
|
+
const manufacturerPartId = parseInt(sellerPart['manufacturer_part']);
|
|
161
|
+
if (!(manufacturerPartId in lastRowDict)) {
|
|
162
|
+
const lastRow = $(`#sourcing-${manufacturerPartId}-last`);
|
|
163
|
+
if (lastRow.length === 0) {
|
|
164
|
+
lastRowDict[manufacturerPartId] = $(`#sourcing-mp-${manufacturerPartId}`);
|
|
165
|
+
} else {
|
|
166
|
+
lastRowDict[manufacturerPartId] = lastRow;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
if (lastRowDict[manufacturerPartId]) {
|
|
170
|
+
const pn = sellerPart['seller_part_number'];
|
|
171
|
+
const moq = sellerPart['minimum_order_quantity'];
|
|
172
|
+
const mpq = sellerPart['minimum_pack_quantity'];
|
|
173
|
+
const price = parseFloat(sellerPart['unit_cost']).toFixed(4);
|
|
174
|
+
const leadTime = `${sellerPart['lead_time_days']}`;
|
|
175
|
+
const nre = parseFloat(sellerPart['nre_cost']).toFixed(4);
|
|
176
|
+
const ncnr = "True";
|
|
177
|
+
lastRowDict[manufacturerPartId].after(`<tr data-tt-id="${index + 1}" data-tt-parent-id="${manufacturerPartId}"><td></td><td></td><td>${mouserImage} Mouser</td><td>${pn}</td><td>${moq}</td><td>${mpq}</td><td>${price}</td><td>${leadTime}</td><td>${nre}</td><td>${ncnr}</td><td>(${stock} in stock)</td></tr>`)
|
|
178
|
+
const lastRow = lastRowDict[manufacturerPartId].closest('tr').next('tr');
|
|
179
|
+
lastRowDict[manufacturerPartId] = lastRow;
|
|
180
|
+
const parentNode = $("#manufacturer-parts").treetable("node", manufacturerPartId);
|
|
181
|
+
$("#manufacturer-parts").treetable("loadBranch", parentNode, lastRow);
|
|
182
|
+
$("#manufacturer-parts").treetable("collapseAll");
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
if (optimalSellerPart && optimalSellerPart !== "None") {
|
|
187
|
+
const mpBaseId = `#sourcing-mp-${optimalSellerPart['manufacturer_part']}`;
|
|
188
|
+
if (optimalSellerPart['seller'] === null) { // TODO need to fix this to get the seller name via the API
|
|
189
|
+
$(`${mpBaseId}-name`).text(optimalSellerPart['data_source']).prepend(mouserImage);
|
|
190
|
+
}
|
|
191
|
+
$(`${mpBaseId}-pn`).text(optimalSellerPart['seller_part_number']);
|
|
192
|
+
$(`${mpBaseId}-moq`).text(optimalSellerPart['minimum_order_quantity']);
|
|
193
|
+
$(`${mpBaseId}-mpq`).text(optimalSellerPart['minimum_pack_quantity']);
|
|
194
|
+
$(`${mpBaseId}-cost`).text(optimalSellerPart['unit_cost']);
|
|
195
|
+
$(`${mpBaseId}-lt`).text(optimalSellerPart['lead_time_days']);
|
|
196
|
+
$(`${mpBaseId}-nre`).text(optimalSellerPart['nre_cost']);
|
|
197
|
+
$(`${mpBaseId}-ncnr`).text(optimalSellerPart['ncnr']);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function updateSpecs(unitCost, estCostPerOrder, specsMissingParts, dataSheet, mouserStock, sellerLink) {
|
|
202
|
+
const mouserEstCost = $("#mouser-est-cost");
|
|
203
|
+
const mouserEstCostPerOrder = $("#mouser-est-cost-per-order");
|
|
204
|
+
const mouserMissingCosts = $("#mouser-missing-item-costs");
|
|
205
|
+
const mouserDataSheetWrapperElement = $("#mouser-data-sheet-wrapper");
|
|
206
|
+
const mouserDataSheetElement = $("#mouser-data-sheet");
|
|
207
|
+
const mouserStockWrapperElement = $("#mouser-stock-wrapper");
|
|
208
|
+
const mouserStockElement = $("#mouser-stock");
|
|
209
|
+
const currencyUnit = "{{ organization.currency }}" === "USD" ? "$" : ""; // Do this right some day...
|
|
210
|
+
updateSpecElement(mouserEstCost, unitCost, `${currencyUnit}${unitCost}`);
|
|
211
|
+
updateSpecElement(mouserEstCostPerOrder, estCostPerOrder, `${currencyUnit}${estCostPerOrder}`);
|
|
212
|
+
updateSpecElement(mouserMissingCosts, specsMissingParts, `${specsMissingParts}`);
|
|
213
|
+
updateSpecElement(mouserStockElement, mouserStock, `${mouserStock}`, mouserStockWrapperElement, false);
|
|
214
|
+
mouserStockElement.append(` (<a href="${sellerLink}" target="_blank">view</a>)`)
|
|
215
|
+
|
|
216
|
+
if (dataSheet) {
|
|
217
|
+
mouserDataSheetElement.attr("href", `${dataSheet}`);
|
|
218
|
+
mouserDataSheetWrapperElement.show();
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function updateSpecElement(element, property, text, wrapper = null, hasDefault = true) {
|
|
223
|
+
if (property != null) {
|
|
224
|
+
if (hasDefault) {
|
|
225
|
+
element.text(text + ' ').prepend(mouserImage).next().css('color', '#bdbdbd').prepend('(').append(' without Mouser)');
|
|
226
|
+
} else {
|
|
227
|
+
element.text(text).prepend(mouserImage);
|
|
228
|
+
if (wrapper) {
|
|
229
|
+
wrapper.show();
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function updateIndented(indentedBomParts) {
|
|
236
|
+
const baseUnitCostId = "#bom-indented-unit-cost-";
|
|
237
|
+
const baseSellerNameId = "#bom-indented-seller-name-";
|
|
238
|
+
updateBom(indentedBomParts, baseUnitCostId, baseSellerNameId);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function updateFlat(flatBomParts) {
|
|
242
|
+
const baseUnitCostId = "#bom-flat-unit-cost-";
|
|
243
|
+
const baseSellerNameId = "#bom-flat-seller-name-";
|
|
244
|
+
updateBom(flatBomParts, baseUnitCostId, baseSellerNameId);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function updateBom(bomParts, baseUnitCostId, baseSellerNameId) {
|
|
248
|
+
for (const partRevId in bomParts) {
|
|
249
|
+
if (bomParts.hasOwnProperty(partRevId) && bomParts[partRevId].hasOwnProperty('seller_part') && bomParts[partRevId].hasOwnProperty('api_info')) {
|
|
250
|
+
const unitCost = parseFloat(bomParts[partRevId]['seller_part']['unit_cost']);
|
|
251
|
+
const sellerName = bomParts[partRevId]['seller_part']['data_source'];
|
|
252
|
+
const isMouser = bomParts[partRevId]['seller_part']['seller'] === 'None' || bomParts[partRevId]['seller_part']['seller'] === null;
|
|
253
|
+
const sellerLink = bomParts[partRevId]['api_info']['product_detail_url'];
|
|
254
|
+
const sourcingInfo = bomParts[partRevId]['api_info'] !== 'None';
|
|
255
|
+
const unitCostId = `${baseUnitCostId}${partRevId}`;
|
|
256
|
+
const unitCostElement = $(unitCostId);
|
|
257
|
+
const sellerNameId = `${baseSellerNameId}${partRevId}`;
|
|
258
|
+
const sellerNameElement = $(sellerNameId);
|
|
259
|
+
if (unitCost && sourcingInfo && sellerLink) {
|
|
260
|
+
unitCostElement.text('');
|
|
261
|
+
unitCostElement.html(`<a href="${sellerLink}" target="_blank">${unitCost.toFixed(4)}</a>`);
|
|
262
|
+
} else if (unitCost && sourcingInfo) {
|
|
263
|
+
unitCostElement.text(`${unitCost.toFixed(4)}`);
|
|
264
|
+
}
|
|
265
|
+
if (unitCost && sourcingInfo && bomParts[partRevId]['seller_part']['data_source'] === 'Mouser') unitCostElement.append(mouserImage);
|
|
266
|
+
if (isMouser && sellerName) sellerNameElement.text('Mouser');
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
</script>
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{# To include this widget, make sure to pass: `manufacturer_parts`, `profile`, (optional, for mouser) `part_revision` variables #}
|
|
2
|
+
{% load static %}
|
|
3
|
+
|
|
4
|
+
{% block head %}
|
|
5
|
+
<link rel="stylesheet" type="text/css" href="{% static 'bom/css/treetable-theme.css' %}"/>
|
|
6
|
+
<link rel="stylesheet" type="text/css" href="{% static 'bom/css/jquery.treetable.css' %}"/>
|
|
7
|
+
{% endblock %}
|
|
8
|
+
|
|
9
|
+
<div class="responsive-table-wrapper">
|
|
10
|
+
<table id="seller-parts" class="highlight tight treetable">
|
|
11
|
+
<thead>
|
|
12
|
+
<tr>
|
|
13
|
+
<th>Part</th>
|
|
14
|
+
<th>Manufacturer</th>
|
|
15
|
+
<th>MPN</th>
|
|
16
|
+
<th>Seller</th>
|
|
17
|
+
<th>PN</th>
|
|
18
|
+
<th>MOQ</th>
|
|
19
|
+
<th>MPQ</th>
|
|
20
|
+
<th>Price</th>
|
|
21
|
+
<th>Lead Time (days)</th>
|
|
22
|
+
<th>NRE</th>
|
|
23
|
+
<th>NCNR</th>
|
|
24
|
+
<th colspan="2">Manage</th>
|
|
25
|
+
</tr>
|
|
26
|
+
</thead>
|
|
27
|
+
<tbody>
|
|
28
|
+
{% for seller_part in seller_parts %}
|
|
29
|
+
<tr data-tt-id="0" data-tt-parent-id="{{ seller_part.id }}" {% if forloop.last %}id="sourcing-{{ seller_part.id }}-last"{% endif %}>
|
|
30
|
+
<td><a href="{% url 'bom:part-info' part_id=seller_part.manufacturer_part.part_id %}">{{ seller_part.manufacturer_part.part }}</a></td>
|
|
31
|
+
<td>{% if seller_part.manufacturer_part.manufacturer %}<a href="{% url 'bom:manufacturer-info' manufacturer_id=seller_part.manufacturer_part.manufacturer_id %}">{% endif %}{{ seller_part.manufacturer_part.manufacturer }}{% if seller_part.manufacturer_part.manufacturer %}</a>{% endif %}</td>
|
|
32
|
+
<td>{{ seller_part.manufacturer_part.manufacturer_part_number }}</td>
|
|
33
|
+
<td>{{ seller_part.seller.name }}</td>
|
|
34
|
+
<td>{{ seller_part.seller_part_number }}</td>
|
|
35
|
+
<td>{{ seller_part.minimum_order_quantity }}</td>
|
|
36
|
+
<td>{{ seller_part.minimum_pack_quantity }}</td>
|
|
37
|
+
<td>{{ seller_part.unit_cost }}</td>
|
|
38
|
+
<td>{{ seller_part.lead_time_days }}</td>
|
|
39
|
+
<td>{{ seller_part.nre_cost }}</td>
|
|
40
|
+
<td>{{ seller_part.ncnr }}</td>
|
|
41
|
+
<td>{% if profile.role == 'A' %}
|
|
42
|
+
<a href="{% url 'bom:sellerpart-edit' sellerpart_id=seller_part.id %}">edit</a> |
|
|
43
|
+
<a href="{% url 'bom:sellerpart-delete' sellerpart_id=seller_part.id %}">delete</a>
|
|
44
|
+
{% endif %}</td>
|
|
45
|
+
</tr>
|
|
46
|
+
{% empty %}
|
|
47
|
+
<tr data-tt-id="0" data-tt-parent-id="{{ seller_part.id }}">
|
|
48
|
+
<td colspan="99" style="text-align: center;"><i>No seller parts found.</i></td>
|
|
49
|
+
</tr>
|
|
50
|
+
{% endfor %}
|
|
51
|
+
</tbody>
|
|
52
|
+
</table>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
<!-- Floating Horizontal Scrollbar -->
|
|
57
|
+
<script type="text/javascript" src="{% static 'bom/js/jquery.ba-floatingscrollbar.min.js' %}"></script>
|
|
58
|
+
<script>
|
|
59
|
+
$(function () {
|
|
60
|
+
$('.responsive-table-wrapper').floatingScrollbar();
|
|
61
|
+
});
|
|
62
|
+
</script>
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{% extends 'bom/bom-base.html' %}
|
|
2
|
+
|
|
3
|
+
{% load static %}
|
|
4
|
+
{% load materializecss %}
|
|
5
|
+
|
|
6
|
+
{% block head-title %}{{ title }}{% endblock %}
|
|
7
|
+
|
|
8
|
+
{% block bom-menu %}
|
|
9
|
+
<li><a href="{% url 'bom:home' %}">Cancel</a></li>
|
|
10
|
+
{% endblock %}
|
|
11
|
+
|
|
12
|
+
{% block content %}
|
|
13
|
+
<div class="container-app">
|
|
14
|
+
{% if profile.role == 'A' %}
|
|
15
|
+
<div class="row">
|
|
16
|
+
<form name="form" action="{% url 'bom:create-part' %}" method="post" class="col s12">
|
|
17
|
+
{% csrf_token %}
|
|
18
|
+
<h2>Part Number</h2>
|
|
19
|
+
{% if organization.number_scheme == 'S' %}
|
|
20
|
+
<p>Part numbers must be in the format CCC-{{ organization.number_ns }}-VV where CCC is the part number class, {{ organization.number_ns }} is the part number number item, and VV
|
|
21
|
+
is the part variation. To change the part number item length or to create part classes, go to the <a
|
|
22
|
+
href="{% url 'bom:settings' tab_anchor='indabom' %}">Settings</a> page, and create them in the IndaBOM tab.</p>
|
|
23
|
+
{% endif %}
|
|
24
|
+
<div class="row">
|
|
25
|
+
{{ part_form.non_field_errors }}
|
|
26
|
+
{{ part_form|materializecss:'m4 s12' }}
|
|
27
|
+
</div>
|
|
28
|
+
<h2>Part Properties</h2>
|
|
29
|
+
{% include 'bom/part-revision-display.html' with part_revision_form=part_revision_form %}
|
|
30
|
+
<h2>Add a Manufacturer Part</h2>
|
|
31
|
+
<p><i>The below fields are optional.</i></p>
|
|
32
|
+
<p>Skip this form if your part is a custom part. Leave the manufacturer field unselected if creating a new manufacturer.</p>
|
|
33
|
+
<div class="row">
|
|
34
|
+
{{ manufacturer_form.non_field_errors }}
|
|
35
|
+
{{ manufacturer_part_form.manufacturer_part_number.errors }}
|
|
36
|
+
{{ manufacturer_part_form.manufacturer_part_number|materializecss:'m4 s12' }}
|
|
37
|
+
{{ manufacturer_part_form.manufacturer.errors }}
|
|
38
|
+
{{ manufacturer_part_form.manufacturer|materializecss:'m4 s12' }}
|
|
39
|
+
<div class="input-field col m4 s12">
|
|
40
|
+
{{ manufacturer_form.name.errors }}
|
|
41
|
+
{{ manufacturer_form.name }}
|
|
42
|
+
<label for="{{ manufacturer_form.name.id_for_label }}">OR Create A New Manufacturer:</label>
|
|
43
|
+
</div>
|
|
44
|
+
<div class="input-field col m4 s12 materialize-checkbox-padding">
|
|
45
|
+
<span>Select for parts that aren't found on Mouser.com</span>
|
|
46
|
+
{{ manufacturer_part_form.mouser_disable.errors }}
|
|
47
|
+
{{ manufacturer_part_form.mouser_disable|materializecss }}
|
|
48
|
+
</div>
|
|
49
|
+
</div>
|
|
50
|
+
<div class="row">
|
|
51
|
+
<div class="col s6">
|
|
52
|
+
<a href="javascript:history.back()" class="waves-effect waves-light btn-flat grey-text lighten-1" style="margin-left: -16px;">Cancel</a>
|
|
53
|
+
</div>
|
|
54
|
+
<div class="col s6 right-align">
|
|
55
|
+
<button class="waves-effect waves-light btn btn-primary" type="submit" name="action">Save
|
|
56
|
+
</button>
|
|
57
|
+
</div>
|
|
58
|
+
</div>
|
|
59
|
+
</form>
|
|
60
|
+
</div>
|
|
61
|
+
{% else %}
|
|
62
|
+
{% include 'bom/nothing-to-see.html' with required_privilege='Admin' %}
|
|
63
|
+
{% endif %}
|
|
64
|
+
</div>
|
|
65
|
+
{% endblock %}
|