django-bom 1.228__tar.gz → 1.261__tar.gz

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.

Files changed (213) hide show
  1. {django-bom-1.228/django_bom.egg-info → django_bom-1.261}/PKG-INFO +27 -20
  2. {django-bom-1.228 → django_bom-1.261}/README.md +7 -8
  3. {django-bom-1.228 → django_bom-1.261}/bom/admin.py +61 -14
  4. django_bom-1.261/bom/auth_backends.py +47 -0
  5. {django-bom-1.228 → django_bom-1.261}/bom/constants.py +9 -0
  6. {django-bom-1.228 → django_bom-1.261}/bom/csv_headers.py +23 -44
  7. django_bom-1.261/bom/forms.py +1328 -0
  8. {django-bom-1.228 → django_bom-1.261}/bom/helpers.py +81 -5
  9. django_bom-1.261/bom/local_settings.py +35 -0
  10. django_bom-1.261/bom/migrations/0046_alter_sellerpart_unique_together.py +17 -0
  11. django_bom-1.261/bom/migrations/0047_sellerpart_seller_part_number.py +18 -0
  12. django_bom-1.261/bom/migrations/0048_rename_part_organization_number_class_bom_part_organiz_b333d6_idx_and_more.py +1017 -0
  13. django_bom-1.261/bom/migrations/0049_alter_assembly_id_alter_assemblysubparts_id_and_more.py +99 -0
  14. django_bom-1.261/bom/migrations/0050_alter_organization_options.py +17 -0
  15. django_bom-1.261/bom/migrations/0051_alter_manufacturer_organization_and_more.py +41 -0
  16. django_bom-1.261/bom/migrations/0052_remove_partrevision_attribute_and_more.py +584 -0
  17. {django-bom-1.228 → django_bom-1.261}/bom/models.py +302 -173
  18. {django-bom-1.228 → django_bom-1.261}/bom/part_bom.py +1 -0
  19. django_bom-1.261/bom/settings.py +262 -0
  20. django_bom-1.261/bom/static/bom/css/materialize.min.css +6 -0
  21. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/css/style.css +115 -5
  22. django_bom-1.261/bom/static/bom/doc/test_full_bom.csv +37 -0
  23. django_bom-1.261/bom/static/bom/js/formset-handler.js +65 -0
  24. django_bom-1.261/bom/static/bom/js/materialize.min.js +6 -0
  25. django_bom-1.261/bom/templates/bom/account-delete.html +23 -0
  26. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/add-manufacturer-part.html +2 -5
  27. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/add-sellerpart.html +9 -5
  28. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/base-menu.html +10 -1
  29. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/base.html +8 -0
  30. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/bom-action-table.html +2 -2
  31. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/bom-base.html +0 -7
  32. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/bom-form-modal.html +3 -2
  33. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/bom-form.html +1 -5
  34. django_bom-1.261/bom/templates/bom/bom-modal-add-users.html +49 -0
  35. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/bom-signup.html +0 -4
  36. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/components/bom-flat.html +2 -2
  37. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/components/bom-indented.html +3 -3
  38. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/components/manufacturer-part-list.html +6 -2
  39. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/components/seller-part-list.html +2 -1
  40. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/create-part.html +2 -5
  41. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/dashboard.html +6 -3
  42. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/edit-manufacturer-part.html +2 -5
  43. django_bom-1.261/bom/templates/bom/edit-part-class.html +120 -0
  44. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/edit-part.html +2 -6
  45. django_bom-1.261/bom/templates/bom/edit-quantity-of-measure.html +119 -0
  46. django_bom-1.261/bom/templates/bom/edit-user-meta.html +70 -0
  47. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/help.html +2 -9
  48. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/manufacturer-info.html +1 -2
  49. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/manufacturers.html +3 -2
  50. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/organization-create.html +8 -8
  51. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/part-info.html +14 -6
  52. django_bom-1.261/bom/templates/bom/part-revision-display.html +50 -0
  53. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/part-revision-edit.html +2 -5
  54. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/part-revision-manage-bom.html +1 -2
  55. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/part-revision-new.html +2 -5
  56. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/part-revision-release.html +0 -7
  57. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/search-help.html +0 -4
  58. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/seller-info.html +1 -2
  59. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/sellers.html +3 -2
  60. django_bom-1.261/bom/templates/bom/settings.html +592 -0
  61. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/signup.html +1 -1
  62. django_bom-1.261/bom/templates/bom/subscription_panel.html +16 -0
  63. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/upload-bom.html +0 -1
  64. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/upload-parts-help.html +3 -6
  65. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/upload-parts.html +0 -4
  66. {django-bom-1.228 → django_bom-1.261}/bom/tests.py +128 -37
  67. {django-bom-1.228 → django_bom-1.261}/bom/third_party_apis/mouser.py +1 -0
  68. {django-bom-1.228 → django_bom-1.261}/bom/urls.py +20 -8
  69. {django-bom-1.228 → django_bom-1.261}/bom/views/json_views.py +2 -2
  70. {django-bom-1.228 → django_bom-1.261}/bom/views/views.py +211 -56
  71. {django-bom-1.228 → django_bom-1.261/django_bom.egg-info}/PKG-INFO +27 -20
  72. {django-bom-1.228 → django_bom-1.261}/django_bom.egg-info/SOURCES.txt +14 -5
  73. django_bom-1.261/django_bom.egg-info/requires.txt +6 -0
  74. django_bom-1.261/pyproject.toml +70 -0
  75. django_bom-1.261/setup.cfg +4 -0
  76. django-bom-1.228/bom/forms.py +0 -1399
  77. django-bom-1.228/bom/local_settings.py +0 -41
  78. django-bom-1.228/bom/settings.py +0 -225
  79. django-bom-1.228/bom/static/.DS_Store +0 -0
  80. django-bom-1.228/bom/static/bom/.DS_Store +0 -0
  81. django-bom-1.228/bom/static/bom/css/materialize.min.css +0 -13
  82. django-bom-1.228/bom/static/bom/doc/test_full_bom.csv +0 -36
  83. django-bom-1.228/bom/static/bom/img/.DS_Store +0 -0
  84. django-bom-1.228/bom/static/bom/js/materialize.min.js +0 -6
  85. django-bom-1.228/bom/templates/bom/edit-part-class.html +0 -36
  86. django-bom-1.228/bom/templates/bom/edit-user-meta.html +0 -41
  87. django-bom-1.228/bom/templates/bom/part-revision-display.html +0 -184
  88. django-bom-1.228/bom/templates/bom/settings.html +0 -395
  89. django-bom-1.228/django_bom.egg-info/requires.txt +0 -5
  90. django-bom-1.228/setup.cfg +0 -25
  91. django-bom-1.228/setup.py +0 -43
  92. {django-bom-1.228 → django_bom-1.261}/LICENSE +0 -0
  93. {django-bom-1.228 → django_bom-1.261}/MANIFEST.in +0 -0
  94. {django-bom-1.228 → django_bom-1.261}/bom/__init__.py +0 -0
  95. {django-bom-1.228 → django_bom-1.261}/bom/apps.py +0 -0
  96. {django-bom-1.228 → django_bom-1.261}/bom/base_classes.py +0 -0
  97. {django-bom-1.228 → django_bom-1.261}/bom/context_processors.py +0 -0
  98. {django-bom-1.228 → django_bom-1.261}/bom/decorators.py +0 -0
  99. {django-bom-1.228 → django_bom-1.261}/bom/form_fields.py +0 -0
  100. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0001_initial.py +0 -0
  101. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0002_auto_20180908_2151.py +0 -0
  102. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0003_sellerpart_data_source.py +0 -0
  103. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0004_auto_20180911_0011.py +0 -0
  104. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0005_auto_20181007_1934.py +0 -0
  105. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0006_auto_20181007_1949.py +0 -0
  106. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0007_auto_20181009_0256.py +0 -0
  107. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0008_auto_20181030_0427.py +0 -0
  108. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0009_subpart_reference.py +0 -0
  109. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0010_auto_20181202_0733.py +0 -0
  110. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0011_auto_20181202_2113.py +0 -0
  111. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0012_partchangehistory.py +0 -0
  112. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0013_auto_20190222_1631.py +0 -0
  113. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0014_auto_20190223_2353.py +0 -0
  114. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0015_auto_20190303_1915.py +0 -0
  115. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0016_auto_20190405_2308.py +0 -0
  116. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0017_auto_20190616_1912.py +0 -0
  117. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0018_auto_20190616_2143.py +0 -0
  118. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0019_auto_20190624_1246.py +0 -0
  119. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0020_auto_20190627_0207.py +0 -0
  120. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0021_auto_20190627_0428.py +0 -0
  121. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0022_auto_20190811_2140.py +0 -0
  122. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0023_auto_20191205_2351.py +0 -0
  123. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0024_auto_20191214_1342.py +0 -0
  124. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0025_auto_20191221_1907.py +0 -0
  125. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0026_auto_20191222_2258.py +0 -0
  126. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0027_auto_20191222_2347.py +0 -0
  127. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0028_partrevision_displayable_synopsis.py +0 -0
  128. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0029_auto_20191231_1630.py +0 -0
  129. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0030_auto_20200101_2253.py +0 -0
  130. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0031_auto_20200104_1352.py +0 -0
  131. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0032_auto_20200126_1806.py +0 -0
  132. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0033_auto_20200203_0618.py +0 -0
  133. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0034_auto_20200222_0359.py +0 -0
  134. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0035_auto_20200303_0111.py +0 -0
  135. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0036_auto_20200303_0538.py +0 -0
  136. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0037_auto_20200405_1642.py +0 -0
  137. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0038_auto_20200422_0504.py +0 -0
  138. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0039_auto_20200929_2315.py +0 -0
  139. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0040_alter_organization_currency.py +0 -0
  140. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0041_organization_subscription_quantity.py +0 -0
  141. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0042_auto_20210720_2137.py +0 -0
  142. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0043_auto_20211123_0157.py +0 -0
  143. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0044_auto_20220831_1241.py +0 -0
  144. {django-bom-1.228 → django_bom-1.261}/bom/migrations/0045_sellerpart_link.py +0 -0
  145. {django-bom-1.228 → django_bom-1.261}/bom/migrations/__init__.py +0 -0
  146. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/css/dashboard.css +0 -0
  147. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/css/jquery.treetable.css +0 -0
  148. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/css/part-info.css +0 -0
  149. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/css/tablesorter-theme.materialize.css +0 -0
  150. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/css/treetable-theme.css +0 -0
  151. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/doc/sample_part_classes.csv +0 -0
  152. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/doc/test_bom.csv +0 -0
  153. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/doc/test_bom_5_intelligent.csv +0 -0
  154. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/doc/test_new_parts.csv +0 -0
  155. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/doc/test_new_parts_5_intelligent.csv +0 -0
  156. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/_ionicons_svg_md-arrow-dropdown.svg +0 -0
  157. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/_ionicons_svg_md-arrow-dropright.svg +0 -0
  158. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/favicon.ico +0 -0
  159. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/1x/btn_google_signin_dark_disabled_web.png +0 -0
  160. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/1x/btn_google_signin_dark_focus_web.png +0 -0
  161. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/1x/btn_google_signin_dark_normal_web.png +0 -0
  162. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/1x/btn_google_signin_dark_pressed_web.png +0 -0
  163. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/1x/btn_google_signin_light_disabled_web.png +0 -0
  164. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/1x/btn_google_signin_light_focus_web.png +0 -0
  165. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/1x/btn_google_signin_light_normal_web.png +0 -0
  166. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/1x/btn_google_signin_light_pressed_web.png +0 -0
  167. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/2x/btn_google_signin_dark_disabled_web@2x.png +0 -0
  168. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/2x/btn_google_signin_dark_focus_web@2x.png +0 -0
  169. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/2x/btn_google_signin_dark_normal_web@2x.png +0 -0
  170. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/2x/btn_google_signin_dark_pressed_web@2x.png +0 -0
  171. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/2x/btn_google_signin_light_disabled_web@2x.png +0 -0
  172. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/2x/btn_google_signin_light_focus_web@2x.png +0 -0
  173. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/2x/btn_google_signin_light_normal_web@2x.png +0 -0
  174. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/2x/btn_google_signin_light_pressed_web@2x.png +0 -0
  175. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_dark_disabled_ios.eps +0 -0
  176. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_dark_disabled_ios.svg +0 -0
  177. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_dark_focus_ios.eps +0 -0
  178. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_dark_focus_ios.svg +0 -0
  179. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_dark_normal_ios.eps +0 -0
  180. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_dark_normal_ios.svg +0 -0
  181. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_dark_pressed_ios.eps +0 -0
  182. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_dark_pressed_ios.svg +0 -0
  183. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_light_disabled_ios.eps +0 -0
  184. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_light_disabled_ios.svg +0 -0
  185. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_light_focus_ios.eps +0 -0
  186. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_light_focus_ios.svg +0 -0
  187. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_light_normal_ios.eps +0 -0
  188. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_light_normal_ios.svg +0 -0
  189. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_light_pressed_ios.eps +0 -0
  190. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google/web/vector/btn_google_light_pressed_ios.svg +0 -0
  191. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/google_drive_logo.svg +0 -0
  192. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/indabom.png +0 -0
  193. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/mouser.png +0 -0
  194. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/img/octopart_blue.svg +0 -0
  195. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/js/jquery-3.4.1.min.js +0 -0
  196. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/js/jquery.ba-floatingscrollbar.min.js +0 -0
  197. {django-bom-1.228 → django_bom-1.261}/bom/static/bom/js/jquery.treetable.js +0 -0
  198. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/bom-action-btn.html +0 -0
  199. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/bom-base-menu.html +0 -0
  200. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/dashboard-menu.html +0 -0
  201. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/nothing-to-see.html +0 -0
  202. {django-bom-1.228 → django_bom-1.261}/bom/templates/bom/table_of_contents.html +0 -0
  203. {django-bom-1.228 → django_bom-1.261}/bom/templates/registration/login.html +0 -0
  204. {django-bom-1.228 → django_bom-1.261}/bom/third_party_apis/__init__.py +0 -0
  205. {django-bom-1.228 → django_bom-1.261}/bom/third_party_apis/base_api.py +0 -0
  206. {django-bom-1.228 → django_bom-1.261}/bom/third_party_apis/google_drive.py +0 -0
  207. {django-bom-1.228 → django_bom-1.261}/bom/third_party_apis/test_apis.py +0 -0
  208. {django-bom-1.228 → django_bom-1.261}/bom/utils.py +0 -0
  209. {django-bom-1.228 → django_bom-1.261}/bom/validators.py +0 -0
  210. {django-bom-1.228 → django_bom-1.261}/bom/views/__init__.py +0 -0
  211. {django-bom-1.228 → django_bom-1.261}/bom/wsgi.py +0 -0
  212. {django-bom-1.228 → django_bom-1.261}/django_bom.egg-info/dependency_links.txt +0 -0
  213. {django-bom-1.228 → django_bom-1.261}/django_bom.egg-info/top_level.txt +0 -0
@@ -1,24 +1,34 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: django-bom
3
- Version: 1.228
3
+ Version: 1.261
4
4
  Summary: A simple Django app to manage a bill of materials.
5
- Home-page: https://www.indabom.com/
6
- Author: Mike Kasparian
7
- Author-email: mpkasp@gmail.com
8
- License: GPL 3.0 License
9
- Platform: UNKNOWN
5
+ Author-email: Mike Kasparian <mpkasp@gmail.com>
6
+ License: GPL-3.0-only
7
+ Project-URL: Homepage, https://www.indabom.com/
10
8
  Classifier: Environment :: Web Environment
11
9
  Classifier: Framework :: Django
12
- Classifier: Framework :: Django :: 3.1
10
+ Classifier: Framework :: Django :: 5
13
11
  Classifier: Intended Audience :: Developers
14
- Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
15
13
  Classifier: Operating System :: OS Independent
16
14
  Classifier: Programming Language :: Python
17
- Classifier: Programming Language :: Python :: 3.7
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3 :: Only
18
20
  Classifier: Topic :: Internet :: WWW/HTTP
19
21
  Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
22
+ Requires-Python: >=3.10
20
23
  Description-Content-Type: text/markdown
21
24
  License-File: LICENSE
25
+ Requires-Dist: Django>=5.2
26
+ Requires-Dist: social-auth-app-django<6,>=5
27
+ Requires-Dist: social-auth-core<6,>=4
28
+ Requires-Dist: google-api-python-client>=2
29
+ Requires-Dist: django-materializecss-form
30
+ Requires-Dist: django-money<5,>=3
31
+ Dynamic: license-file
22
32
 
23
33
  # BOM
24
34
  ![](https://github.com/mpkasp/django-bom/workflows/Django%20CI/badge.svg)
@@ -41,16 +51,15 @@ If you already have a django project, you can skip to [Add Django Bom To Your Ap
41
51
  * [Installation pitfalls](#installation-pitfalls)
42
52
 
43
53
  ## Start From Scratch: Add to a new Django project
44
- 1. To start from scratch we recommend setting up a virtual environment
54
+ 1. To start from scratch, install your pipenv using Python 3.12
45
55
  ```
46
- virtualenv -p python3 mysite
47
- cd mysite
48
- source bin/activate
56
+ pipenv --python 3.12
57
+ pipenv shell
49
58
  ```
50
59
 
51
60
  2. From here install django, and set up your project.
52
61
  ```
53
- pip install django
62
+ pipenv install django
54
63
  django-admin startproject mysite
55
64
  cd mysite
56
65
  python manage.py migrate
@@ -60,7 +69,7 @@ python manage.py createsuperuser # make a user for your development environment
60
69
  3. Continue on to [Add Django Bom To Your App](#add-django-bom-to-your-app).
61
70
 
62
71
  ## Add Django Bom To Your App
63
- django-bom is a [reusable django application](https://docs.djangoproject.com/en/1.11/intro/reusable-apps/). If you don't already have a django project, you can follow some quick steps below to get up and running, or read about creating your first django app [here](https://docs.djangoproject.com/en/1.11/intro/tutorial01/). Note that django-bom currently supports python 2.7.
72
+ django-bom is a [reusable django application](https://docs.djangoproject.com/en/1.11/intro/reusable-apps/). If you don't already have a django project, you can follow some quick steps below to get up and running, or read about creating your first django app [here](https://docs.djangoproject.com/en/1.11/intro/tutorial01/).
64
73
 
65
74
  ```
66
75
  pip install django-bom
@@ -110,7 +119,7 @@ TEMPLATES = [
110
119
  and
111
120
 
112
121
  ```
113
- BOM_CONFIG = {}
122
+ BOM_CONFIG = {'standalone_mode': False}
114
123
  ```
115
124
 
116
125
  4. Run `python manage.py migrate` to create the bom models.
@@ -132,7 +141,7 @@ source bin/activate
132
141
  2. From here install django, and set up your project.
133
142
  ```
134
143
  git clone https://github.com/mpkasp/django-bom.git
135
- pip install -r requirements.txt
144
+ pipenv install
136
145
  python manage.py migrate
137
146
  cp bom/local_settings.py.example bom/local_settings.py
138
147
  python manage.py runserver
@@ -195,5 +204,3 @@ You may get an error during your `pip install -r requirements.txt` related to sq
195
204
 
196
205
  #### Cryptography
197
206
  Sometimes you'll have issues installing cryptography, if this is the case you may just need to set up some environment variables. This [stackoverflow](https://stackoverflow.com/questions/46288737/error-while-installing-sqlite-using-pip-on-python-2-7-13) may help.
198
-
199
-
@@ -19,16 +19,15 @@ If you already have a django project, you can skip to [Add Django Bom To Your Ap
19
19
  * [Installation pitfalls](#installation-pitfalls)
20
20
 
21
21
  ## Start From Scratch: Add to a new Django project
22
- 1. To start from scratch we recommend setting up a virtual environment
22
+ 1. To start from scratch, install your pipenv using Python 3.12
23
23
  ```
24
- virtualenv -p python3 mysite
25
- cd mysite
26
- source bin/activate
24
+ pipenv --python 3.12
25
+ pipenv shell
27
26
  ```
28
27
 
29
28
  2. From here install django, and set up your project.
30
29
  ```
31
- pip install django
30
+ pipenv install django
32
31
  django-admin startproject mysite
33
32
  cd mysite
34
33
  python manage.py migrate
@@ -38,7 +37,7 @@ python manage.py createsuperuser # make a user for your development environment
38
37
  3. Continue on to [Add Django Bom To Your App](#add-django-bom-to-your-app).
39
38
 
40
39
  ## Add Django Bom To Your App
41
- django-bom is a [reusable django application](https://docs.djangoproject.com/en/1.11/intro/reusable-apps/). If you don't already have a django project, you can follow some quick steps below to get up and running, or read about creating your first django app [here](https://docs.djangoproject.com/en/1.11/intro/tutorial01/). Note that django-bom currently supports python 2.7.
40
+ django-bom is a [reusable django application](https://docs.djangoproject.com/en/1.11/intro/reusable-apps/). If you don't already have a django project, you can follow some quick steps below to get up and running, or read about creating your first django app [here](https://docs.djangoproject.com/en/1.11/intro/tutorial01/).
42
41
 
43
42
  ```
44
43
  pip install django-bom
@@ -88,7 +87,7 @@ TEMPLATES = [
88
87
  and
89
88
 
90
89
  ```
91
- BOM_CONFIG = {}
90
+ BOM_CONFIG = {'standalone_mode': False}
92
91
  ```
93
92
 
94
93
  4. Run `python manage.py migrate` to create the bom models.
@@ -110,7 +109,7 @@ source bin/activate
110
109
  2. From here install django, and set up your project.
111
110
  ```
112
111
  git clone https://github.com/mpkasp/django-bom.git
113
- pip install -r requirements.txt
112
+ pipenv install
114
113
  python manage.py migrate
115
114
  cp bom/local_settings.py.example bom/local_settings.py
116
115
  python manage.py runserver
@@ -1,3 +1,4 @@
1
+ from django.conf import settings
1
2
  from django.contrib import admin
2
3
  from django.contrib.auth import get_user_model
3
4
  from django.contrib.auth.admin import UserAdmin
@@ -6,29 +7,31 @@ from .models import (
6
7
  Assembly,
7
8
  Manufacturer,
8
9
  ManufacturerPart,
9
- Organization,
10
10
  Part,
11
11
  PartClass,
12
12
  PartRevision,
13
+ PartRevisionProperty,
14
+ PartRevisionPropertyDefinition,
13
15
  Seller,
14
16
  SellerPart,
15
17
  Subpart,
16
- UserMeta,
18
+ UnitDefinition,
19
+ QuantityOfMeasure,
20
+ get_organization_model,
21
+ get_user_meta_model
17
22
  )
18
23
 
19
-
20
24
  User = get_user_model()
25
+ UserMeta = get_user_meta_model()
26
+ Organization = get_organization_model()
21
27
 
22
28
  class UserMetaInline(admin.TabularInline):
23
29
  model = UserMeta
30
+ verbose_name = 'BOM User Meta'
24
31
  raw_id_fields = ('organization',)
25
32
  can_delete = False
26
33
 
27
34
 
28
- class UserAdmin(UserAdmin):
29
- inlines = (UserMetaInline,)
30
-
31
-
32
35
  class OrganizationAdmin(admin.ModelAdmin):
33
36
  list_display = ('name',)
34
37
 
@@ -52,6 +55,7 @@ class SellerPartAdmin(admin.ModelAdmin):
52
55
  list_display = (
53
56
  'manufacturer_part',
54
57
  'seller',
58
+ 'seller_part_number',
55
59
  'minimum_order_quantity',
56
60
  'minimum_pack_quantity',
57
61
  'unit_cost',
@@ -87,7 +91,10 @@ class PartClassAdmin(admin.ModelAdmin):
87
91
 
88
92
  class PartRevisionAdminInline(admin.TabularInline):
89
93
  model = PartRevision
94
+ extra = 0
90
95
  raw_id_fields = ('assembly',)
96
+ readonly_fields = ('timestamp',)
97
+ show_change_link = True
91
98
 
92
99
 
93
100
  class PartAdmin(admin.ModelAdmin):
@@ -97,8 +104,10 @@ class PartAdmin(admin.ModelAdmin):
97
104
  'organization',
98
105
  'get_full_part_number',
99
106
  )
107
+ list_filter = ('organization', 'number_class',)
100
108
  raw_id_fields = ('number_class', 'primary_manufacturer_part',)
101
109
  inlines = [
110
+ PartRevisionAdminInline,
102
111
  ManufacturerPartAdminInline,
103
112
  ]
104
113
 
@@ -109,10 +118,22 @@ class PartAdmin(admin.ModelAdmin):
109
118
  get_full_part_number.admin_order_field = 'number_class__part_number'
110
119
 
111
120
 
121
+ class QuantityOfMeasureAdmin(admin.ModelAdmin):
122
+ list_display = ('name', 'organization')
123
+ list_filter = ('organization',)
124
+
125
+
126
+ class PartRevisionPropertyInline(admin.TabularInline):
127
+ model = PartRevisionProperty
128
+ extra = 1
129
+ raw_id_fields = ('property_definition', 'part_revision', 'unit_definition')
130
+
131
+
112
132
  class PartRevisionAdmin(admin.ModelAdmin):
113
133
  list_display = ('part', 'revision', 'description', 'get_assembly_size', 'timestamp',)
114
134
  raw_id_fields = ('assembly',)
115
135
  readonly_fields = ('timestamp',)
136
+ inlines = [PartRevisionPropertyInline]
116
137
 
117
138
  def get_assembly_size(self, obj):
118
139
  return None if obj.assembly is None else obj.assembly.subparts.count()
@@ -141,14 +162,37 @@ class AssemblyAdmin(admin.ModelAdmin):
141
162
  ]
142
163
 
143
164
 
144
- # Try to unregister User model
145
- try:
146
- admin.site.unregister(User)
147
- except admin.sites.NotRegistered:
148
- pass
165
+ class UnitDefinitionAdmin(admin.ModelAdmin):
166
+ list_display = ('name', 'symbol', 'quantity_of_measure', 'base_multiplier', 'organization')
167
+ list_filter = ('organization',)
168
+
169
+
170
+ class PartRevisionPropertyDefinitionAdmin(admin.ModelAdmin):
171
+ list_display = ('code', 'name', 'type', 'organization', 'quantity_of_measure')
172
+ list_filter = ('organization',)
173
+
174
+
175
+ if settings.BOM_USER_META_MODEL == 'bom.UserMeta':
176
+ current_user_admin = admin.site._registry.get(User)
177
+
178
+ if current_user_admin:
179
+ user_admin_class = current_user_admin.__class__
180
+ existing_inlines = list(user_admin_class.inlines or [])
181
+ if UserMetaInline not in existing_inlines:
182
+ existing_inlines.append(UserMetaInline)
183
+ user_admin_class.inlines = existing_inlines
184
+ else:
185
+ class BomUserAdmin(UserAdmin):
186
+ inlines = [UserMetaInline]
187
+
188
+
189
+ admin.site.register(User, BomUserAdmin)
190
+
191
+ if settings.BOM_ORGANIZATION_MODEL == 'bom.Organization':
192
+ from .models import Organization
193
+
194
+ admin.site.register(Organization, OrganizationAdmin)
149
195
 
150
- admin.site.register(User, UserAdmin)
151
- admin.site.register(Organization, OrganizationAdmin)
152
196
  admin.site.register(Seller, SellerAdmin)
153
197
  admin.site.register(SellerPart, SellerPartAdmin)
154
198
  admin.site.register(ManufacturerPart, ManufacturerPartAdmin)
@@ -158,3 +202,6 @@ admin.site.register(PartRevision, PartRevisionAdmin)
158
202
  admin.site.register(Manufacturer, ManufacturerAdmin)
159
203
  admin.site.register(Assembly, AssemblyAdmin)
160
204
  admin.site.register(Subpart, SubpartAdmin)
205
+ admin.site.register(UnitDefinition, UnitDefinitionAdmin)
206
+ admin.site.register(PartRevisionPropertyDefinition, PartRevisionPropertyDefinitionAdmin)
207
+ admin.site.register(QuantityOfMeasure, QuantityOfMeasureAdmin)
@@ -0,0 +1,47 @@
1
+ from typing import Optional
2
+
3
+ from . import constants
4
+ from .models import get_organization_model
5
+
6
+ Organization = get_organization_model()
7
+
8
+
9
+ class OrganizationPermissionBackend:
10
+ """
11
+ Object-level permission backend for django-bom.
12
+
13
+ - Uses Django's has_perm(user, perm, obj) to evaluate permissions tied to an Organization.
14
+ - Superusers are granted all permissions.
15
+ - For `bom.manage_members`: User must be owner or admin within the organization.
16
+ """
17
+
18
+ def authenticate(self, request, **credentials): # pragma: no cover - not used for auth
19
+ return None
20
+
21
+ def has_perm(self, user_obj, perm: str, obj: Optional[object] = None):
22
+ # Only handle our specific object-level permission. Let other backends process others.
23
+ if not user_obj or not user_obj.is_authenticated:
24
+ return False
25
+
26
+ if user_obj.is_superuser:
27
+ return True
28
+
29
+ if perm != 'bom.manage_members':
30
+ return None
31
+
32
+ if obj is None or not isinstance(obj, Organization):
33
+ return False
34
+
35
+ profile = user_obj.bom_profile()
36
+ if not profile or profile.organization_id != obj.id:
37
+ return False
38
+
39
+ if obj.subscription != constants.SUBSCRIPTION_TYPE_PRO:
40
+ return False
41
+
42
+ is_owner = obj.owner_id == user_obj.id
43
+ is_admin = getattr(profile, 'role', None) == constants.ROLE_TYPE_ADMIN
44
+ if not (is_owner or is_admin):
45
+ return False
46
+
47
+ return True
@@ -34,6 +34,13 @@ DATA_SOURCES = (
34
34
  (DATA_SOURCE_MOUSER, 'mouser'),
35
35
  )
36
36
 
37
+ PART_REVISION_PROPERTY_TYPE_STRING = 'S'
38
+ PART_REVISION_PROPERTY_TYPE_DECIMAL = 'D'
39
+ PART_REVISION_PROPERTY_TYPE_BOOLEAN = 'B'
40
+ PART_REVISION_PROPERTY_TYPES = ((PART_REVISION_PROPERTY_TYPE_STRING, 'String'),
41
+ (PART_REVISION_PROPERTY_TYPE_DECIMAL, 'Number'),
42
+ (PART_REVISION_PROPERTY_TYPE_BOOLEAN, 'Boolean'),)
43
+
37
44
  VALUE_UNITS = (
38
45
  NO_CHOICE,
39
46
  ('Ohms', '\u03A9'),
@@ -92,6 +99,8 @@ PACKAGE_TYPES = (
92
99
  ('QFT', 'QFT'),
93
100
  ('PLCC', 'PLCC'),
94
101
  ('VGA', 'VGA'),
102
+ ('BGA', 'BGA'),
103
+ ('CSP', 'CSP'),
95
104
  ('Other', 'Other'),
96
105
  )
97
106
 
@@ -35,8 +35,11 @@ class CSVHeader:
35
35
  class CSVHeaders(ABC):
36
36
  all_headers_defns = []
37
37
 
38
+ def __init__(self, *args, **kwargs):
39
+ self.headers_defns = list(self.all_headers_defns)
40
+
38
41
  def get_synoynms(self, hdr_name):
39
- for defn in self.all_headers_defns:
42
+ for defn in self.headers_defns:
40
43
  if hdr_name in defn:
41
44
  return defn.synonyms()
42
45
  else:
@@ -53,7 +56,7 @@ class CSVHeaders(ABC):
53
56
 
54
57
  # Preserves order of definitions as listed in all_header_defns:
55
58
  def get_default_all(self):
56
- return [d.name for d in self.all_headers_defns]
59
+ return [d.name for d in self.headers_defns]
57
60
 
58
61
  # Given a list of header names returns the default name for each. The return list
59
62
  # matches the order of the input list. If a name is not recognized, then returns
@@ -151,6 +154,9 @@ class CSVHeaders(ABC):
151
154
  return True
152
155
  raise CSVHeaderError(f'Missing column named {header}')
153
156
 
157
+ def add_header(self, header):
158
+ self.headers_defns.append(header)
159
+
154
160
 
155
161
  #
156
162
  # For each CSV header class, a static data member dictionary uses the key as the default name for the header while the
@@ -172,6 +178,7 @@ class ManufacturerPartCSVHeaders(CSVHeaders):
172
178
  class SellerPartCSVHeaders(CSVHeaders):
173
179
  all_headers_defns = [
174
180
  CSVHeader('seller', name_options=['part_seller', 'part_seller_name', ]),
181
+ CSVHeader('seller_part_number', name_options=['seller_pn', 'spn', 'pn', 'part_seller_part_number', 'seller_part_number', ]),
175
182
  CSVHeader('unit_cost', name_options=['seller_part_unit_cost', 'unit_cost', 'part_cost', ]),
176
183
  CSVHeader('minimum_order_quantity', name_options=['minimum_order_quantity', 'moq', 'part_moq', ]),
177
184
  CSVHeader('nre_cost', name_options=['part_nre', 'part_nre_cost', 'nre', 'nre_cost', ]),
@@ -189,52 +196,25 @@ class PartClassesCSVHeaders(CSVHeaders):
189
196
  ]
190
197
 
191
198
 
192
- class PartsListCSVHeaders(CSVHeaders):
193
- part_attributes = [
194
- CSVHeader('value', name_options=['val', 'val.', ]),
195
- CSVHeader('value_units', name_options=['value units', 'val. units', 'val units', ]),
196
- CSVHeader('tolerance', name_options=[]),
197
- CSVHeader('attribute', name_options=[]),
198
- CSVHeader('package', name_options=[]),
199
- CSVHeader('pin_count', name_options=[]),
200
- CSVHeader('frequency', name_options=[]),
201
- CSVHeader('frequency_units', name_options=[]),
202
- CSVHeader('wavelength', name_options=[]),
203
- CSVHeader('wavelength_units', name_options=[]),
204
- CSVHeader('memory', name_options=[]),
205
- CSVHeader('memory_units', name_options=[]),
206
- CSVHeader('interface', name_options=[]),
207
- CSVHeader('supply_voltage', name_options=[]),
208
- CSVHeader('supply_voltage_units', name_options=[]),
209
- CSVHeader('temperature_rating', name_options=[]),
210
- CSVHeader('temperature_rating_units', name_options=[]),
211
- CSVHeader('power_rating', name_options=[]),
212
- CSVHeader('power_rating_units', name_options=[]),
213
- CSVHeader('voltage_rating', name_options=[]),
214
- CSVHeader('voltage_rating_units', name_options=[]),
215
- CSVHeader('current_rating', name_options=[]),
216
- CSVHeader('current_rating_units', name_options=[]),
217
- CSVHeader('material', name_options=[]),
218
- CSVHeader('color', name_options=[]),
219
- CSVHeader('finish', name_options=[]),
220
- CSVHeader('length', name_options=[]),
221
- CSVHeader('length_units', name_options=[]),
222
- CSVHeader('width', name_options=[]),
223
- CSVHeader('width_units', name_options=[]),
224
- CSVHeader('height', name_options=[]),
225
- CSVHeader('height_units', name_options=[]),
226
- CSVHeader('weight', name_options=[]),
227
- CSVHeader('weight_units', name_options=[]),
228
- ]
199
+ class PartRevisionPropertyCSVHeaders(CSVHeaders):
200
+ def add_dynamic_headers(self, definitions):
201
+ for defn in definitions:
202
+ self.add_header(
203
+ CSVHeader(defn.form_field_name,
204
+ name_options=[defn.name, f'property_{defn.code}', f'property_{defn.name}', 'p']))
205
+ if defn.quantity_of_measure:
206
+ self.add_header(
207
+ CSVHeader(defn.form_unit_field_name, name_options=[f'{defn.name} Units', f'{defn.code}_units']))
208
+
229
209
 
210
+ class PartsListCSVHeaders(PartRevisionPropertyCSVHeaders):
230
211
  all_headers_defns = [
231
212
  CSVHeader('description', name_options=['desc', 'desc.', ]),
232
213
  CSVHeader('manufacturer_name', name_options=['mfg_name', 'manufacturer_name', 'part_manufacturer', 'mfg', 'manufacturer', 'manufacturer name', ]),
233
214
  CSVHeader('manufacturer_part_number', name_options=['mpn', 'mfg_part_number', 'part_manufacturer_part_number', 'mfg part number', 'manufacturer part number']),
234
215
  CSVHeader('part_number', name_options=['part number', 'part no', ]),
235
216
  CSVHeader('revision', name_options=['rev', 'part_revision', ]),
236
- ] + part_attributes \
237
- + SellerPartCSVHeaders.all_headers_defns
217
+ ] + SellerPartCSVHeaders.all_headers_defns
238
218
 
239
219
 
240
220
  class PartsListCSVHeadersSemiIntelligent(PartsListCSVHeaders):
@@ -245,11 +225,10 @@ class PartsListCSVHeadersSemiIntelligent(PartsListCSVHeaders):
245
225
  CSVHeader('part_class', name_options=['class', 'part_category']),
246
226
  CSVHeader('part_number', name_options=['part number', 'part no', ]),
247
227
  CSVHeader('revision', name_options=['rev', 'part_revision', ]),
248
- ] + PartsListCSVHeaders.part_attributes \
249
- + SellerPartCSVHeaders.all_headers_defns
228
+ ] + SellerPartCSVHeaders.all_headers_defns
250
229
 
251
230
 
252
- class BOMFlatCSVHeaders(CSVHeaders):
231
+ class BOMFlatCSVHeaders(PartRevisionPropertyCSVHeaders):
253
232
  all_headers_defns = [
254
233
  CSVHeader('part_number', name_options=['part number', 'part no', ]),
255
234
  CSVHeader('quantity', name_options=['count', 'qty', 'quantity', ]),