Compare commits

...

885 Commits

Author SHA1 Message Date
Oliver
5ec9989229 Merge pull request #2182 from inventree/0.5.3
0.5.3
2021-10-20 11:25:28 +11:00
Oliver
ca3b6e62cf Increment version number 2021-10-20 10:55:20 +11:00
Oliver
838f412c6b Merge pull request #2122 from SchrodingersGat/bom-speed-improvements
Temporary fix for BOM speed improvements

(cherry picked from commit 8dddb200c7)
2021-10-20 10:53:46 +11:00
Oliver
bfbd604d0a Merge pull request #2146 from SchrodingersGat/0.5.2
0.5.2
2021-10-19 10:05:54 +11:00
Oliver
a75535d336 Increment version number 2021-10-19 09:46:39 +11:00
Oliver
f333532a1f Bug fix: allow empty barcode field when receiving purchase orders 2021-10-19 09:40:19 +11:00
Oliver
36d6628eb6 Revert PR which allowed hiding of major UI functions 2021-10-13 09:57:19 +11:00
Oliver
f948290b21 Merge pull request #2091 from inventree/0.5.1
0.5.1
2021-10-11 21:20:15 +11:00
Oliver
e44446793d Merge remote-tracking branch 'inventree/l10_crowdin' into 0.5.1
# Conflicts:
#	InvenTree/locale/de/LC_MESSAGES/django.po
#	InvenTree/locale/el/LC_MESSAGES/django.po
#	InvenTree/locale/es/LC_MESSAGES/django.po
#	InvenTree/locale/fr/LC_MESSAGES/django.po
#	InvenTree/locale/he/LC_MESSAGES/django.po
#	InvenTree/locale/id/LC_MESSAGES/django.po
#	InvenTree/locale/it/LC_MESSAGES/django.po
#	InvenTree/locale/ja/LC_MESSAGES/django.po
#	InvenTree/locale/ko/LC_MESSAGES/django.po
#	InvenTree/locale/nl/LC_MESSAGES/django.po
#	InvenTree/locale/no/LC_MESSAGES/django.po
#	InvenTree/locale/pl/LC_MESSAGES/django.po
#	InvenTree/locale/ru/LC_MESSAGES/django.po
#	InvenTree/locale/sv/LC_MESSAGES/django.po
#	InvenTree/locale/th/LC_MESSAGES/django.po
#	InvenTree/locale/tr/LC_MESSAGES/django.po
#	InvenTree/locale/vi/LC_MESSAGES/django.po
#	InvenTree/locale/zh/LC_MESSAGES/django.po
2021-10-11 21:02:06 +11:00
Oliver
cfde81d09f Merge remote-tracking branch 'inventree/l10' into 0.5.1
# Conflicts:
#	InvenTree/locale/de/LC_MESSAGES/django.po
#	InvenTree/locale/el/LC_MESSAGES/django.po
#	InvenTree/locale/es/LC_MESSAGES/django.po
#	InvenTree/locale/fr/LC_MESSAGES/django.po
#	InvenTree/locale/he/LC_MESSAGES/django.po
#	InvenTree/locale/id/LC_MESSAGES/django.po
#	InvenTree/locale/it/LC_MESSAGES/django.po
#	InvenTree/locale/ja/LC_MESSAGES/django.po
#	InvenTree/locale/ko/LC_MESSAGES/django.po
#	InvenTree/locale/nl/LC_MESSAGES/django.po
#	InvenTree/locale/no/LC_MESSAGES/django.po
#	InvenTree/locale/pl/LC_MESSAGES/django.po
#	InvenTree/locale/ru/LC_MESSAGES/django.po
#	InvenTree/locale/sv/LC_MESSAGES/django.po
#	InvenTree/locale/th/LC_MESSAGES/django.po
#	InvenTree/locale/tr/LC_MESSAGES/django.po
#	InvenTree/locale/vi/LC_MESSAGES/django.po
#	InvenTree/locale/zh/LC_MESSAGES/django.po
2021-10-11 21:00:08 +11:00
Oliver
4628bb8f08 Simplify version numbering scheme 2021-10-11 18:56:38 +11:00
github-actions[bot]
dc17e3998a updated translation base 2021-10-11 07:56:21 +00:00
github-actions[bot]
5e8e900b04 updated translation base 2021-10-11 06:22:12 +00:00
github-actions[bot]
94c25c93d6 updated translation base 2021-10-11 01:01:51 +00:00
github-actions[bot]
9e41eb23ac updated translation base 2021-10-11 00:43:52 +00:00
github-actions[bot]
99352f8f84 updated translation base 2021-10-11 00:25:33 +00:00
Oliver
bfb162c688 Merge pull request #2130 from SchrodingersGat/links-fix
Fix various documentation links

(cherry picked from commit 49601d2b7e)
2021-10-11 11:24:16 +11:00
github-actions[bot]
12d3646da1 updated translation base 2021-10-11 00:00:43 +00:00
github-actions[bot]
a21e4560f1 updated translation base 2021-10-10 23:55:19 +00:00
Oliver
66c037b9f8 0.5 -> 0.5.1 2021-10-11 10:43:36 +11:00
Oliver
66d4b14ba4 Fix conflicts 2021-10-11 10:42:23 +11:00
github-actions[bot]
1c912088a2 updated translation base 2021-10-10 23:41:54 +00:00
github-actions[bot]
793fe39fe7 updated translation base 2021-10-08 02:10:20 +00:00
github-actions[bot]
a6f5a8107a updated translation base 2021-10-07 11:45:06 +00:00
github-actions[bot]
d3cdb34151 updated translation base 2021-10-07 02:55:56 +00:00
github-actions[bot]
638b478d1f updated translation base 2021-10-07 01:16:12 +00:00
Oliver
4efa8a5d3b Merge pull request #2118 from SchrodingersGat/bom-export-fix
Fix error on BOM export

(cherry picked from commit a69bcb9f18)
2021-10-07 12:15:11 +11:00
github-actions[bot]
1273d93c8c updated translation base 2021-10-07 00:01:36 +00:00
github-actions[bot]
db59f99f2d updated translation base 2021-10-06 11:41:52 +00:00
github-actions[bot]
7df5215404 updated translation base 2021-10-06 10:38:04 +00:00
github-actions[bot]
6e47a1feb6 updated translation base 2021-10-06 10:05:12 +00:00
github-actions[bot]
88464ad640 updated translation base 2021-10-06 09:54:20 +00:00
github-actions[bot]
32556d660e updated translation base 2021-10-06 09:53:08 +00:00
Oliver
ca0caa3d2b Merge pull request #2112 from SchrodingersGat/docs-link-fix
Fix docs link for release versions

(cherry picked from commit c2d33588d0)
2021-10-06 20:53:06 +11:00
Oliver
f6bcee06cb Merge pull request #2108 from SchrodingersGat/sales-order-table-fixes
Bug fixes for SalesOrderLineItem table

(cherry picked from commit 6706d6c768)
2021-10-06 17:27:47 +11:00
github-actions[bot]
08394574ce updated translation base 2021-10-06 06:23:34 +00:00
github-actions[bot]
4a74294123 updated translation base 2021-10-05 22:53:58 +00:00
github-actions[bot]
e84cad1660 updated translation base 2021-10-05 07:03:31 +00:00
github-actions[bot]
86c8d86b67 updated translation base 2021-10-05 02:21:40 +00:00
github-actions[bot]
c567b7a84c updated translation base 2021-10-05 01:54:15 +00:00
Oliver
1132b6c51a Fixes for build output tables
- Only show "completed" builds in the "completed builds" table (should be obvious)
- Display "serial number" appropriately in build output allocation table

(cherry picked from commit a3ba33cae8)
2021-10-05 12:53:17 +11:00
Oliver
10e3a5f5a9 Merge pull request #2100 from SchrodingersGat/barcode-field-fix
Fix for "barcode" field in purchase order receive serializer

(cherry picked from commit 5c26769999)
2021-10-05 12:34:46 +11:00
github-actions[bot]
95bf39c127 updated translation base 2021-10-05 01:33:19 +00:00
eeintech
f661a4f4ec Added migration file
(cherry picked from commit a735a3e15c)
2021-10-05 10:58:28 +11:00
eeintech
3d067b39b1 Fix plural name for Companies in Admin interface
(cherry picked from commit 6e31a8111b)
2021-10-05 10:58:22 +11:00
github-actions[bot]
d01686248b updated translation base 2021-10-04 23:41:41 +00:00
Oliver
094a63f751 Bump version number -> 0.5.1 2021-10-04 09:52:15 +11:00
github-actions[bot]
024552e4d0 updated translation base 2021-10-03 22:36:56 +00:00
github-actions[bot]
a5e26ceeac updated translation base 2021-10-02 15:35:10 +00:00
Oliver
2fedc1267c Merge pull request #2090 from SchrodingersGat/po-api-fix
Fix for purchase order API

(cherry picked from commit b7ff50ca87)
2021-10-03 01:33:54 +10:00
Oliver
7d3c0a7aa8 Merge pull request #2088 from inventree/0.5.0
Fixes for docker build steps
2021-10-01 13:46:34 +10:00
Oliver
018ab0cd05 Fixes for docker build steps 2021-10-01 13:26:43 +10:00
Oliver
0b5a4efef6 Merge pull request #2050 from inventree/0.5.0
0.5.0
2021-10-01 13:23:13 +10:00
Oliver
a3c2f8b36b Merge remote-tracking branch 'inventree/l10_crowdin' into 0.5.0 2021-10-01 09:18:45 +10:00
github-actions[bot]
4893cd527f updated translation base 2021-09-30 22:48:24 +00:00
Oliver
32e82488d3 Merge pull request #2084 from eeintech/show_part_delete
Show part delete button when part still active

(cherry picked from commit 16dcd8adf6)
2021-10-01 08:46:46 +10:00
github-actions[bot]
f2050f7cab updated translation base 2021-09-30 12:16:50 +00:00
Oliver
827534138b Merge pull request #2086 from SchrodingersGat/search-fix
Fix for search page

(cherry picked from commit 172d184a4d)
2021-09-30 22:14:09 +10:00
github-actions[bot]
1ef1fdc6e6 updated translation base 2021-09-30 00:47:58 +00:00
github-actions[bot]
7f93b37437 updated translation base 2021-09-29 23:44:17 +00:00
Oliver
9c2f4ce491 Merge pull request #2083 from SchrodingersGat/search-results
Display "full_name" rather than "name" in quick search bar

(cherry picked from commit 819934af7e)
2021-09-30 09:43:15 +10:00
github-actions[bot]
d56da99c0d updated translation base 2021-09-28 00:58:41 +00:00
Oliver
79686ebb2a Merge pull request #2082 from SchrodingersGat/stock-item-delete
Override the "delete" behaviour for StockItem API

(cherry picked from commit 344383d3d4)
2021-09-28 10:57:43 +10:00
github-actions[bot]
71b3dd3e76 updated translation base 2021-09-27 23:30:47 +00:00
github-actions[bot]
dcdb2add28 updated translation base 2021-09-27 04:34:36 +00:00
Oliver
d14b763ef9 Merge pull request #2079 from SchrodingersGat/url-fix
URL fixes

(cherry picked from commit b623f34881)
2021-09-27 14:34:28 +10:00
github-actions[bot]
7522a80f96 updated translation base 2021-09-24 02:37:15 +00:00
Oliver
fb27eb48c4 Merge remote-tracking branch 'inventree/master' into 0.5.0 2021-09-24 12:36:49 +10:00
Oliver
a1d54690c2 Merge pull request #2075 from SchrodingersGat/default-supplier-fix
Default supplier fix

(cherry picked from commit b3c8bd7779)
2021-09-24 12:36:19 +10:00
Oliver
b3c8bd7779 Merge pull request #2075 from SchrodingersGat/default-supplier-fix
Default supplier fix
2021-09-24 12:35:28 +10:00
Oliver
f53aac0784 Remove "default_supplier" field when duplicating a part 2021-09-24 12:04:36 +10:00
Oliver
d2b9993e96 Fix form filters for "default_supplier" 2021-09-24 12:04:25 +10:00
github-actions[bot]
5efba2dad0 updated translation base 2021-09-23 11:53:15 +00:00
Oliver
d7ac9978eb Merge pull request #2071 from inventree/dependabot/pip/django-3.2.5
Build(deps): Bump django from 3.2.4 to 3.2.5

(cherry picked from commit 39cab4690d)
2021-09-23 21:52:02 +10:00
Oliver
39cab4690d Merge pull request #2071 from inventree/dependabot/pip/django-3.2.5
Build(deps): Bump django from 3.2.4 to 3.2.5
2021-09-23 21:51:23 +10:00
dependabot[bot]
1d85ccd543 Build(deps): Bump django from 3.2.4 to 3.2.5
Bumps [django](https://github.com/django/django) from 3.2.4 to 3.2.5.
- [Release notes](https://github.com/django/django/releases)
- [Commits](https://github.com/django/django/compare/3.2.4...3.2.5)

---
updated-dependencies:
- dependency-name: django
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-09-22 17:46:34 +00:00
github-actions[bot]
d943020d56 updated translation base 2021-09-21 23:36:06 +00:00
Oliver
80d2fc4b9e Merge pull request #2068 from rocheparadox/master
css for part to occupy full height - fix for bug Inventree#1848

(cherry picked from commit b123135df6)
2021-09-22 09:34:37 +10:00
Oliver
2e1277839e Merge pull request #2067 from SchrodingersGat/warning-improvements
Improve 'health status' warnings
2021-09-22 09:34:23 +10:00
github-actions[bot]
a41db6ae28 updated translation base 2021-09-21 23:08:30 +00:00
Oliver
b123135df6 Merge pull request #2068 from rocheparadox/master
css for part to occupy full height - fix for bug Inventree#1848
2021-09-22 09:06:03 +10:00
Oliver
3b763e95fd Improve 'health status' warnings
- Don't show error message if only non-critical warnings present

(cherry picked from commit 5443beef65)
2021-09-21 09:27:46 +10:00
rocheparadox
c1a827819c css for part to occupy full height - fix for bug Inventree#1848 2021-09-20 17:00:45 +05:30
Oliver
5443beef65 Improve 'health status' warnings
- Don't show error message if only non-critical warnings present
2021-09-20 17:15:31 +10:00
github-actions[bot]
11d2d5588f updated translation base 2021-09-17 12:36:18 +00:00
Oliver
ae3c9817be Merge pull request #2062 from SchrodingersGat/app-issue-template
Add template for app issues
2021-09-17 22:34:45 +10:00
Oliver
8d6d16c07b Add template for app issues 2021-09-17 22:13:13 +10:00
github-actions[bot]
7f09ad2b38 updated translation base 2021-09-17 11:49:36 +00:00
Oliver
7b2b01c26f Merge pull request #2060 from SchrodingersGat/character-test
Adds a test for non-standard characters via API
2021-09-17 21:48:10 +10:00
Oliver
e7963f8a85 test fixes 2021-09-17 21:06:07 +10:00
Oliver
11cc0c7ced Adds a test for non-standard characters via API 2021-09-17 20:53:18 +10:00
github-actions[bot]
ebc95cb326 updated translation base 2021-09-14 01:00:20 +00:00
Oliver
d73a7176fd Merge pull request #2055 from SchrodingersGat/test-template-table-fix
Fix callback for test template table
2021-09-14 10:58:57 +10:00
Oliver
44e0fd1a68 Fix callback for test template table
(cherry picked from commit 5bd5c61b9d)
2021-09-14 10:57:55 +10:00
Oliver
5bd5c61b9d Fix callback for test template table 2021-09-14 10:43:40 +10:00
github-actions[bot]
2018229dc5 updated translation base 2021-09-14 00:01:42 +00:00
Oliver
1643a2ccb2 Merge pull request #2054 from matmair/fix-for-2004
Fix for 2004
2021-09-14 10:00:19 +10:00
Matthias
847fb62ab5 js style 2021-09-13 23:25:13 +02:00
Matthias
c890a4a6d0 only add button per name once 2021-09-13 23:18:44 +02:00
github-actions[bot]
224b372eae updated translation base 2021-09-13 10:30:31 +00:00
Oliver
95924852a4 Merge pull request #2053 from SchrodingersGat/contributing
Update contributing.md
2021-09-13 20:29:09 +10:00
github-actions[bot]
7d286cf4b8 updated translation base 2021-09-13 10:04:22 +00:00
Oliver
dfd500097e Update contributing.md
(cherry picked from commit 398842d6e5)
2021-09-13 20:03:31 +10:00
Oliver
7a7684b018 Merge branch 'l10'
# Conflicts:
#	InvenTree/locale/de/LC_MESSAGES/django.po
#	InvenTree/locale/el/LC_MESSAGES/django.po
#	InvenTree/locale/en/LC_MESSAGES/django.po
#	InvenTree/locale/es/LC_MESSAGES/django.po
#	InvenTree/locale/fr/LC_MESSAGES/django.po
#	InvenTree/locale/he/LC_MESSAGES/django.po
#	InvenTree/locale/id/LC_MESSAGES/django.po
#	InvenTree/locale/it/LC_MESSAGES/django.po
#	InvenTree/locale/ja/LC_MESSAGES/django.po
#	InvenTree/locale/ko/LC_MESSAGES/django.po
#	InvenTree/locale/nl/LC_MESSAGES/django.po
#	InvenTree/locale/no/LC_MESSAGES/django.po
#	InvenTree/locale/pl/LC_MESSAGES/django.po
#	InvenTree/locale/ru/LC_MESSAGES/django.po
#	InvenTree/locale/sv/LC_MESSAGES/django.po
#	InvenTree/locale/th/LC_MESSAGES/django.po
#	InvenTree/locale/tr/LC_MESSAGES/django.po
#	InvenTree/locale/vi/LC_MESSAGES/django.po
#	InvenTree/locale/zh/LC_MESSAGES/django.po
2021-09-13 10:15:08 +10:00
Oliver
de565c6e67 Merge branch 'l10' into 0.5.0
# Conflicts:
#	InvenTree/locale/de/LC_MESSAGES/django.po
#	InvenTree/locale/el/LC_MESSAGES/django.po
#	InvenTree/locale/en/LC_MESSAGES/django.po
#	InvenTree/locale/es/LC_MESSAGES/django.po
#	InvenTree/locale/fr/LC_MESSAGES/django.po
#	InvenTree/locale/he/LC_MESSAGES/django.po
#	InvenTree/locale/id/LC_MESSAGES/django.po
#	InvenTree/locale/it/LC_MESSAGES/django.po
#	InvenTree/locale/ja/LC_MESSAGES/django.po
#	InvenTree/locale/ko/LC_MESSAGES/django.po
#	InvenTree/locale/nl/LC_MESSAGES/django.po
#	InvenTree/locale/no/LC_MESSAGES/django.po
#	InvenTree/locale/pl/LC_MESSAGES/django.po
#	InvenTree/locale/ru/LC_MESSAGES/django.po
#	InvenTree/locale/sv/LC_MESSAGES/django.po
#	InvenTree/locale/th/LC_MESSAGES/django.po
#	InvenTree/locale/tr/LC_MESSAGES/django.po
#	InvenTree/locale/vi/LC_MESSAGES/django.po
#	InvenTree/locale/zh/LC_MESSAGES/django.po
2021-09-13 10:03:45 +10:00
Oliver
35dd50e94f Merge branch 'stable' into 0.5.0 2021-09-13 10:00:28 +10:00
Oliver
4b9fd13622 Set version number to 0.5.0 2021-09-13 09:56:53 +10:00
Oliver Walters
06c03039da Add version related tags
- Docs URL points to correct documentation version
- Display if we are in "development" version
2021-09-12 23:13:55 +10:00
Oliver
695370c8d7 Merge pull request #2048 from inventree/version-tags
Updates for docker workflows:
2021-09-12 22:28:26 +10:00
Oliver Walters
7b21270baf Bump version number 2021-09-12 22:08:51 +10:00
Oliver Walters
08397c5e5f different syntax 2021-09-12 22:06:06 +10:00
Oliver Walters
3d4a8bdd22 env prefix 2021-09-12 21:54:59 +10:00
Oliver Walters
06f323fe47 Use GITHUB_BASE_REF 2021-09-12 21:52:02 +10:00
Oliver Walters
6baaf98d7e fix environment variable 2021-09-12 21:49:13 +10:00
Oliver Walters
5770789ddb more fix 2021-09-12 21:40:56 +10:00
Oliver Walters
fd192b65bf workflow fix 2021-09-12 21:37:30 +10:00
Oliver Walters
5ee004eabd Add workflow to check version number 2021-09-12 21:36:14 +10:00
Oliver Walters
429add9000 Updates for docker workflows:
- check_version_number script now handles stable and development codes
2021-09-12 21:17:56 +10:00
Oliver
71ca60d679 Merge pull request #2044 from SchrodingersGat/new-line-item
Bug fix - "Add line item" button
2021-09-08 14:32:51 +10:00
Oliver
b9b23a3853 bug fix 2021-09-08 14:14:14 +10:00
Oliver
829dd0d637 Merge pull request #2043 from SchrodingersGat/received-stock-items
Fixes for purchase order stock table
2021-09-08 13:29:05 +10:00
Oliver
387680fbcd More linting 2021-09-08 13:13:40 +10:00
Oliver
20788bb559 Bump pillow version for security fix 2021-09-08 13:02:49 +10:00
Oliver
a32a30de52 javascript linting 2021-09-08 13:02:03 +10:00
Oliver
1a21576f28 Adds separate option for "prevent_new_stock" to stock_table 2021-09-08 12:57:42 +10:00
Oliver
ba787a0485 Merge pull request #2041 from SchrodingersGat/metadata-fixes
Fix OPTIONS request lookup time
2021-09-08 12:52:00 +10:00
Oliver
8368798280 Move po-line-item-table into javascript file
- for better linting
2021-09-08 12:51:49 +10:00
Oliver
6012855ec4 PEP fixes 2021-09-08 12:24:33 +10:00
Oliver
919b39515f Improve instance lookup for metadata layer
- Existing call to get_object() could take > 20 seconds in some cases
- Not really sure why, some issue with the DRF library
- Was probably parsing the entire queryset rather than doing a PK lookup
- Instead, directly use the provided pk to get the model
2021-09-08 12:18:07 +10:00
Oliver
22aa0a03c9 Merge pull request #2039 from SchrodingersGat/requirements
Re-add gunicorn to requirements file
2021-09-08 08:48:20 +10:00
Oliver
84528df6f8 Re-add gunicorn to requirements file 2021-09-08 08:01:05 +10:00
Oliver
a70f4c86eb Merge pull request #2013 from SchrodingersGat/receive-via-api
Receive via api
2021-09-07 23:56:44 +10:00
Oliver Walters
f349dc01ea JS lint 2021-09-07 23:41:13 +10:00
Oliver Walters
f38bf6e20a Adds unit testing for barcode field 2021-09-07 23:34:14 +10:00
Oliver Walters
bf05c9cfae Adds "barcode" field to POLineItem receive serializer 2021-09-07 23:06:36 +10:00
Oliver Walters
ccb191e5b1 Bump API version 2021-09-07 23:06:17 +10:00
Oliver
50ae331afd Merge pull request #2034 from SchrodingersGat/build-complete-scheduling
Build completion scheduling
2021-09-07 22:51:52 +10:00
Oliver Walters
125554c53f Merge remote-tracking branch 'upstream/master' into receive-via-api
# Conflicts:
#	InvenTree/templates/js/dynamic/inventree.js
#	InvenTree/templates/js/translated/forms.js
#	InvenTree/templates/js/translated/tables.js
2021-09-07 22:34:00 +10:00
Oliver Walters
ecc7bd2d5b Unit test fixes 2021-09-07 22:27:39 +10:00
Oliver
5ab4be7025 Unit test fixes 2021-09-07 17:36:53 +10:00
Oliver
918106c225 Adds a background task to remove StockItem objects which are scheduled for deletion 2021-09-07 16:45:58 +10:00
Oliver
7d3cd03d6c Add "scheduled_for_deletion" field to StockItem
- If set to True, this StockItem will be deleted (soon) by the background worker
- As deletion takes significant time, this prevents delete operations from blocking the UI
2021-09-07 16:28:57 +10:00
Oliver
4b1c2677c5 Update README.md 2021-09-06 09:42:51 +10:00
Oliver
8b6ebc092a Merge pull request #2033 from SchrodingersGat/clip.html-fix
Run HTML linting as .github action
2021-09-04 09:43:14 +10:00
Oliver
f363c0f084 Fix workflow file 2021-09-04 09:22:52 +10:00
Oliver
c7d6b985bd LINT ALL THE THINGS 2021-09-04 09:08:46 +10:00
Oliver
42637ddefa more linting 2021-09-04 08:59:02 +10:00
Oliver
9b001cd298 Another fix 2021-09-04 08:48:34 +10:00
Oliver
f0e5d1984c Fix for about.html 2021-09-04 08:46:30 +10:00
Oliver
0a8365e3bc Fix for clip.html 2021-09-04 08:45:06 +10:00
Oliver
514db57c0c Run HTML linting as .github actino 2021-09-04 08:31:47 +10:00
Oliver
c275bf8d98 Merge pull request #2026 from inventree/js-linting
Js linting
2021-09-03 23:35:14 +10:00
Oliver Walters
e5872f4b67 improve js debug comment 2021-09-03 23:16:35 +10:00
Oliver
888fa51cd7 lots of linting fixes for JS files 2021-09-03 22:54:17 +10:00
Oliver
3db830e7cd Merge pull request #2031 from SchrodingersGat/polineitem-destination
Fix on_delete for PurchaseOrderLineItem.destination
2021-09-02 11:37:54 +10:00
Oliver
5d703af932 Add migration file 2021-09-02 11:11:25 +10:00
Oliver
43c4e936a7 Fix on_delete for PurchaseOrderLineItem.destination
- Currently set to DO_NOTHING
- However when deleting a StockLocation which has a PurchaseOrderLineItem.destionation point to it, this will cause an IntegrityError
2021-09-02 10:41:23 +10:00
Oliver
c90f9b0447 Merge pull request #2030 from matmair/html-code-smells
Html code smells
2021-09-02 08:47:59 +10:00
Matthias
0ebc45e834 replace i html tags 2021-09-02 00:18:47 +02:00
Matthias
6a420fd95c replace bold tags 2021-09-02 00:17:12 +02:00
Oliver
edb1602c7c Merge pull request #2028 from matmair/fix-html-tag
fix html templates
2021-09-02 07:10:15 +10:00
Matthias Mair
2b1279e647 now with closing tags 2021-09-01 08:14:08 +02:00
Matthias Mair
5ad2eabab1 fix html templates 2021-08-31 13:17:38 +02:00
Oliver Walters
cb403a5b29 More work 2021-08-30 22:28:01 +10:00
Oliver Walters
a1b7239b7e indent error 2021-08-30 21:30:10 +10:00
Oliver Walters
6dde353646 Fix indent rule 2021-08-30 21:25:43 +10:00
Oliver Walters
4ffcf48ab3 4 spaces for indent 2021-08-30 21:21:26 +10:00
Oliver Walters
fc125ca20c Fix workflow file 2021-08-30 21:16:54 +10:00
Oliver Walters
9781d585b0 Install eslint-config-google 2021-08-30 21:08:46 +10:00
Oliver Walters
3d5144b9bc Simplify eslintrc file 2021-08-30 20:30:44 +10:00
Oliver Walters
4c39607e00 Brace style fixes 2021-08-30 20:30:26 +10:00
Oliver Walters
e1adef5010 typo fix 2021-08-30 20:08:18 +10:00
Oliver Walters
6177fe0c5a build.js 2021-08-30 19:52:28 +10:00
Oliver Walters
2112c6a9ad barcode.js 2021-08-30 19:33:43 +10:00
Oliver Walters
7d4945d302 company.js 2021-08-30 16:01:45 +10:00
Oliver Walters
0835fe92a0 bom.js 2021-08-30 15:54:31 +10:00
Oliver Walters
51eb40f3bc forms.js 2021-08-30 15:47:58 +10:00
Oliver Walters
bb3b6fdc4d label.js 2021-08-30 15:34:46 +10:00
Oliver Walters
e0e7788af6 More js fixes 2021-08-30 15:32:01 +10:00
Oliver Walters
50b54f0966 order.js 2021-08-30 14:57:17 +10:00
Oliver Walters
00f012311d inventree.js 2021-08-30 14:49:40 +10:00
Oliver Walters
90a75ac7cb nav.js 2021-08-30 14:44:44 +10:00
Oliver Walters
7d5b859233 part.js 2021-08-30 14:39:58 +10:00
Oliver Walters
3e03b1c31d report.js 2021-08-30 14:12:35 +10:00
Oliver Walters
c846aeb60f stock.js 2021-08-30 14:07:34 +10:00
Oliver Walters
65874447ed bug fix 2021-08-30 13:13:12 +10:00
Oliver Walters
5d45fce240 remove import / export calls 2021-08-30 13:10:58 +10:00
Oliver Walters
24ca1c5641 Add "helpers.js" for common functions 2021-08-30 13:06:02 +10:00
Oliver Walters
140a2092c8 fixes for table_filters.js 2021-08-28 22:18:20 +10:00
Oliver Walters
0620e656a0 Fix linting errors or tables.js 2021-08-28 22:12:41 +10:00
Oliver Walters
e85ddf3579 Add required env vars 2021-08-28 21:10:31 +10:00
Oliver Walters
f7c515b889 add "invoke static" step 2021-08-28 21:05:55 +10:00
Oliver Walters
09a7a7d2e4 Install required files 2021-08-28 21:03:09 +10:00
Oliver Walters
d0ccf8647d Add js linting to github workflow 2021-08-28 20:59:41 +10:00
Oliver Walters
f57a31c9b5 Add a test framework script to pull down rendered javascript files
- Use the testing framework so we don't need to spin up a server
2021-08-28 20:46:51 +10:00
Oliver Walters
880a701881 eslint configuration file 2021-08-28 16:55:59 +10:00
Oliver Walters
62d877ba54 Adds script to pull down "rendered" versions of javascript files 2021-08-28 16:40:06 +10:00
Oliver
e261fa6b29 Merge pull request #2024 from SchrodingersGat/category-param-fix
Simple fix for category parameter settings
2021-08-26 23:11:29 +10:00
Oliver Walters
3adf30a00c Simple fix for category parameter settings 2021-08-26 22:50:50 +10:00
Oliver
e5de69cd96 Update version.py
Actually bump the API version
2021-08-26 22:13:13 +10:00
Oliver
320ca451cc Merge pull request #2023 from inventree/units-in-api
Add "units" to PartBriefSerializer
2021-08-26 22:12:27 +10:00
Oliver
610c05384b Merge pull request #2020 from SchrodingersGat/mpn-sorting-fix
Mpn sorting fix
2021-08-26 22:06:06 +10:00
Oliver Walters
ec88415f3d Add "units" to PartBriefSerializer 2021-08-26 21:54:25 +10:00
Oliver
ac8a0be74a Enable sorting by total_price 2021-08-26 08:48:19 +10:00
Oliver
bad246bca6 Fixes for ordering of stock table 2021-08-26 08:24:31 +10:00
Oliver
212a7eeed1 Disable filtering for total_price (as this is not a database field!) 2021-08-26 07:59:47 +10:00
Oliver
8660f13ef5 Allow sorting by purchase price (unit price) 2021-08-26 07:50:19 +10:00
Oliver
9b6b5825f3 Merge pull request #2021 from SchrodingersGat/table-col-fix
Change name of purchaseorder line item table
2021-08-26 07:02:04 +10:00
Oliver
51992a92c1 Change name of purchaseorder line item table
- Was conflicting with purchaseorder table
- Saved column selections were being overridden
2021-08-25 18:00:32 +10:00
Oliver
4b8ef2ad62 Implements custom filtering back end 2021-08-25 17:46:42 +10:00
Oliver
75152fab0e Merge pull request #2018 from SchrodingersGat/file-upload-fix
Fix for file upload bug
2021-08-25 15:05:32 +10:00
Oliver
9c9407b1ab Add unit test for catching bug
- Turns out that in an image was uploaded with more than ~2000 vertical pixels it would crash
- Smaller images worked fine?
2021-08-25 14:48:45 +10:00
Oliver
dcc8acb49a Data must be copied in a particular way 2021-08-25 14:12:26 +10:00
Oliver
44ab487b62 Fix for file upload bug 2021-08-25 12:05:41 +10:00
Oliver
c9756d30bd Add a custom OrderingFilter class
Needs further work
2021-08-25 12:04:15 +10:00
Oliver
2923589c4a Fix sortName for purchase order line item table 2021-08-25 12:02:25 +10:00
Oliver Walters
f6b9d9e6d0 Revert "WIP"
This reverts commit 42af52ee51.
2021-08-24 22:33:29 +10:00
Oliver Walters
42af52ee51 WIP 2021-08-24 22:31:13 +10:00
Oliver
28878b4b0d Update feature_request.md
Slight tweaks
2021-08-24 22:30:01 +10:00
Oliver
18297cd2fb Update bug_report.md
Slight tweaks to bug report template
2021-08-24 22:28:42 +10:00
Oliver
bdcc66bac7 Merge pull request #2009 from matmair/templates
RFC: Issue Templates
2021-08-24 22:26:55 +10:00
Oliver Walters
0d7eb6b72c Style fixes 2021-08-24 22:25:25 +10:00
Oliver Walters
5275d3943b Adds option to hide labels and help text 2021-08-24 22:13:07 +10:00
Oliver Walters
d87ab0033a Add "afterRender" callback for modal forms 2021-08-24 22:05:00 +10:00
Oliver Walters
1360b1592d Some convenience functions 2021-08-24 22:04:43 +10:00
Oliver Walters
e3605001e4 Simple function to render a thumbnail 2021-08-24 22:04:06 +10:00
Oliver Walters
d5e58fd798 Fix for status code serializer 2021-08-24 22:03:52 +10:00
Oliver
86e3e26196 Merge remote-tracking branch 'inventree/master' into receive-via-api 2021-08-24 15:07:55 +10:00
Oliver
0073a77e16 Merge pull request #2008 from matmair/fix-for-1986
Fix for 1986
2021-08-24 14:13:50 +10:00
Oliver
7f75c610ef Merge pull request #2012 from SchrodingersGat/search-preview-click
Wrap search preview results in <a>
2021-08-24 13:47:55 +10:00
Oliver
36a0496dd7 Wrap search preview results in <a>
- Allows clicking with middle mouse (for e.g.) to open in new tab
2021-08-24 13:11:37 +10:00
Oliver
44ecd958a2 Merge pull request #2011 from SchrodingersGat/boolean-form-fixes
Fix for "part" form fields
2021-08-24 12:10:00 +10:00
Oliver
56f27aae23 Merge pull request #2010 from SchrodingersGat/sales-order-tab-fix
Hide "Sales Orders" tab if part is not salable
2021-08-24 12:03:09 +10:00
Oliver
8fc79f45a3 Fix for "part" form fields
- Specify "default" rather than overriding "value"
2021-08-24 11:55:58 +10:00
Oliver
7646535aaf Hide "Sales Orders" tab if part is not salable 2021-08-24 11:48:42 +10:00
Oliver
1b65fbad2c Update unit tests
- Found some bugs too, thanks unit tests!
2021-08-24 11:42:08 +10:00
Oliver
d30173132a Actually receive items 2021-08-24 08:49:23 +10:00
Matthias Mair
446243f2b5 Added templates 2021-08-24 00:29:50 +02:00
Oliver
2aa505b2cb Fix unit tests to match new API format 2021-08-24 08:18:59 +10:00
Oliver
5349addc06 Merge pull request #2005 from eeintech/ui_updates
Updated color of `new` and `add` UI buttons
2021-08-24 08:12:45 +10:00
Matthias
391bb0dbe4 disable ordering on loadStockTable 2021-08-23 22:14:51 +02:00
Matthias
4efcfbecaf refactor stock.js columns into a variable 2021-08-23 22:07:20 +02:00
eeintech
35738ce026 Updated all 'add' buttons to 'btn-success' CSS class 2021-08-23 14:52:35 -04:00
eeintech
6f742319e5 Corrected color of 'new' orders UI button 2021-08-23 12:48:47 -04:00
Oliver
6091f2ba33 Serializer improvements
- Pass the "order" down to the nested serializers for validation
2021-08-24 00:29:38 +10:00
Oliver
dc53a433a7 Fix serializer nesting
- Add new API endpoint to receive items
- Add unit testing
2021-08-23 23:35:22 +10:00
Oliver
28cc241354 Custom DRF serializers for receiving line items against a purchase order 2021-08-23 23:13:07 +10:00
Oliver
f136c974cf Merge pull request #2003 from SchrodingersGat/edit-purchase-prices
Edit purchase prices
2021-08-23 22:54:12 +10:00
Oliver
90d0b8b15d Bump API version 2021-08-23 21:45:40 +10:00
Oliver
5d4f35958d Point table at the new read-only field 2021-08-23 21:45:32 +10:00
Oliver
bb8b85c375 Separate purchase_price and purchase_price_currency for StockItem serializer
- Add "purchase_price_string" for a read-only stringified representation
- Unit testing
2021-08-23 21:44:12 +10:00
Oliver
d267d04bed Allow validation of empty money values 2021-08-23 21:43:31 +10:00
Oliver
f96051d863 Replace print statement with a logger warning 2021-08-23 21:39:54 +10:00
Oliver
8662e6a109 Fix a super annoying validation issue
- Was throwing opaque "too many values to unpack" error
- Simply needed the name of the field.
2021-08-23 21:39:00 +10:00
Oliver
1fb76b9987 Merge pull request #1999 from matmair/turn-of-functions
Disable functions in navbars
2021-08-23 20:29:28 +10:00
Matthias Mair
4c8fdab072 fixed descriptions 2021-08-23 09:44:52 +02:00
Oliver
fd1dd792c6 Merge pull request #1957 from matmair/bpm-purchase-price
BOM - show purchase price
2021-08-23 11:07:30 +10:00
Oliver
87a41c7e39 Merge pull request #2000 from SchrodingersGat/dev-conf-fix
Fix for docker development env file
2021-08-23 09:10:25 +10:00
Oliver
47524f6ea3 Fix for docker development env file 2021-08-23 08:39:09 +10:00
Oliver
a5f26c7f09 Merge pull request #1998 from bobek/patch-1
Fix for production docker environment
2021-08-23 08:38:04 +10:00
Matthias
9fd4b5cce3 use new setting for part-actions
PR complete for #1999
2021-08-22 23:36:49 +02:00
Matthias
b674d851f2 disable fucntions in master nav 2021-08-22 22:30:21 +02:00
Matthias
3682eaac14 disable links in navbar in company views 2021-08-22 22:27:53 +02:00
Matthias
d4efdf86e5 new settingsthe settings-page 2021-08-22 22:09:07 +02:00
Matthias
8edfada22a adding settings definitions for turning off features 2021-08-22 22:08:41 +02:00
Antonin Kral
7f126e58d2 Fix for production docker environment
Values are passed as written resulting in `"WARNING"` being passed to python logger, which will complain and panic. Fix is simply to remove `"` from the value.
2021-08-22 20:59:51 +02:00
Oliver
b6df623554 Merge pull request #1997 from SchrodingersGat/new-po-from-order
Refactor buttons in "order parts" wizard
2021-08-23 00:49:27 +10:00
Oliver Walters
174b7ba7f2 Fix for "new purchase order" button 2021-08-23 00:28:36 +10:00
Oliver Walters
449e0c0af2 Refactor button in first step of "order parts" wizard
- "new supplier part" button was broken
- Was linked to the old "launchModalForm" code
- Now calls createSupplierPart
2021-08-23 00:17:45 +10:00
Oliver
3cf06b4960 Merge pull request #1995 from SchrodingersGat/delete-unused-images
remove some unused images
2021-08-23 00:11:39 +10:00
Oliver Walters
f2e58ebbdd remove some unused images 2021-08-22 23:54:43 +10:00
Oliver
370fbea396 Merge pull request #1987 from matmair/fix-for-1986
Disable table sorting when order is fixed
2021-08-22 23:47:21 +10:00
Oliver
51de0a982a Merge pull request #1990 from matmair/fix-tooltip-linebreak
Fix tooltip linebreak
2021-08-22 08:19:54 +10:00
Matthias
38af66c0de fix linebreak in tooltip
found during fixing #1989
2021-08-21 00:07:45 +02:00
Matthias
8fad704d76 disable ordering on stock 2021-08-20 22:53:25 +02:00
Matthias
5a59a37a89 disable ordering on category 2021-08-20 22:52:57 +02:00
Matthias
109b8c943e disable ordering on part-name 2021-08-20 22:52:14 +02:00
Matthias
7533a9ac0c disable ordering on IPN if fixed ordering 2021-08-20 22:51:36 +02:00
Oliver
8646c73021 Merge pull request #1980 from matmair/housekeeping
Housekeeping
2021-08-20 09:02:33 +10:00
Oliver
5bd31f11eb Merge pull request #1978 from matmair/fix-translate
Fix translations stats - task
2021-08-20 09:01:50 +10:00
Oliver
831ccec399 Merge pull request #1981 from matmair/coverage-to-setup
Merge coverage settings
2021-08-20 09:01:12 +10:00
Matthias Mair
e24a158919 Merge branch 'inventree:master' into bpm-purchase-price 2021-08-20 00:42:50 +02:00
Matthias
c4700d0e10 move coverage settings into setup.cfg 2021-08-20 00:35:25 +02:00
Matthias
e17f15f403 updating year on license 2021-08-20 00:31:10 +02:00
Matthias
e34c27e360 removed translation part - is done now by action 2021-08-20 00:25:55 +02:00
Oliver
414d2dbc96 Merge pull request #1977 from eeintech/company_templates
Fixed company templates
2021-08-20 07:47:29 +10:00
Matthias
575cf87b98 also run static on update 2021-08-19 23:37:38 +02:00
Matthias
ee33de711a move translation stats into own task 2021-08-19 23:36:54 +02:00
Matthias
cb1e7a6cc5 only process purchase_price if prices present 2021-08-19 23:22:58 +02:00
eeintech
ec7392303d Fixed company templates 2021-08-19 10:47:46 -04:00
Oliver
c16ce925dd Merge pull request #1973 from SchrodingersGat/delete-expired-sessions
Run periodic (daily) task to clear out expired sessions
2021-08-19 16:53:19 +10:00
Oliver
f0415640d5 Run periodic (daily) task to clear out expired sessions 2021-08-19 16:34:57 +10:00
Oliver
c3048a1bf1 Merge pull request #1970 from SchrodingersGat/entrypoint
Entrypoint
2021-08-19 12:12:01 +10:00
Oliver
9ed2025021 Add a TODO for future reference 2021-08-19 11:14:13 +10:00
Oliver
52bdfe5465 Env interpolation doesn't seem to work in the CMD 2021-08-18 20:52:14 +10:00
Oliver
eeac561b9b typo fix 2021-08-18 17:07:23 +10:00
Oliver
2095d66677 Fix entrypoint / cmd for production server 2021-08-18 16:29:54 +10:00
Oliver
79d7a9f922 fix typo in dockerfile 2021-08-18 15:16:22 +10:00
Oliver
41db0ff60d Need to specify python3 2021-08-18 14:58:16 +10:00
Oliver
db477bceab typo fix 2021-08-18 14:47:34 +10:00
Oliver
d756579a06 Split production environment variables out into a .env file 2021-08-18 13:02:36 +10:00
Oliver
c1ea6dbb9b Remove commented out functionality from the entrypoint command 2021-08-18 12:28:09 +10:00
Oliver
c2af401854 Pin base python package requirements
- Require invoke to be installed before we can run "invoke update"
2021-08-18 12:03:24 +10:00
Oliver
8fea9bc645 Re-add docker file git version info 2021-08-18 11:25:19 +10:00
Oliver
7bfddd6d51 Simplify init scripts
Single script init.sh which performs the following tasks:
- Creates required directory structure
- Activates python venv (if required)
- Waits for database connection
- Runs command
2021-08-18 09:52:27 +10:00
Oliver
3b8ee48581 Fix env defines in dockerfile 2021-08-18 09:34:09 +10:00
Oliver
da834d8bcc Reduce cruft in logs 2021-08-18 00:04:38 +10:00
Oliver
b48db6f8fe Dockerfile fixes 2021-08-17 23:15:05 +10:00
Oliver
187c9b0971 Add server init script
- Taken (mostly) from https://github.com/inventree/InvenTree/pull/1949
2021-08-17 23:10:57 +10:00
Oliver
8b66babd49 Refactor dockerfile
- Ref: https://github.com/inventree/InvenTree/pull/1949
- Squash all apk commands into single line
- Drop to inventree user rather than running as root
- Separate entrypoint and cmd for each target
- Set the INVENTREE_PY_ENV variable in development mode
2021-08-17 22:58:44 +10:00
Oliver
b6e97f06dd Merge pull request #1968 from SchrodingersGat/docker-internal-vars
Cleanup docker vars for dev setup
2021-08-17 21:18:09 +10:00
Oliver
0294a1c323 Fix for staticfile collection
- Was generating a *lot* of warning messages
- Ref: https://github.com/django-compressor/django-compressor/issues/720
2021-08-17 21:02:45 +10:00
Oliver
895f9f3ce0 Pull debug level out into the .env file 2021-08-17 20:45:57 +10:00
Oliver
d5d89c67b1 Error out if the static or media directories are not properly defined 2021-08-17 20:42:19 +10:00
Oliver
a474000361 Fix critical error in dockerfile
- Don't' be putting no spaces in!
2021-08-17 20:29:48 +10:00
Oliver
7bf3229595 Add comment to docker-compose file 2021-08-17 20:00:54 +10:00
Oliver
07857c3088 Simplify dev-config.env file
- Don't need to re-specify the internal docker variables
- Add comments
2021-08-17 19:59:32 +10:00
Oliver
206743b58d Add a default value for INVENTREE_WEB_ADDR 2021-08-17 19:58:55 +10:00
Oliver
a5808d4360 Merge pull request #1967 from SchrodingersGat/always-translate
Run translation step as part of "update"
2021-08-17 19:52:58 +10:00
Oliver
92aace1278 Run translation step as part of "update" 2021-08-17 18:22:07 +10:00
Guusggg
d8eefec065 Print multi part label (#1963)
* Added description as list for StockLocation

* Merge pull request #1874 from SchrodingersGat/docker-dev-fix

Copy static files when starting dev server

(cherry picked from commit 50eb70f538)

* Merge pull request #1877 from eeintech/fix_search_js

Fixed missing comma propagating to translated JS files

(cherry picked from commit 2009773d9d)

* Merge pull request #1890 from matmair/fix-for-1888

catch connection errors in exchange update

(cherry picked from commit db57e9516b)

* Merge pull request #1887 from matmair/settings-safety

settings fixes

(cherry picked from commit d154ca08ea)

* 0.4.2

* Merge pull request #1894 from SchrodingersGat/non-int-serial-fix

Fix for non-integer serial numbers

(cherry picked from commit 529742b520)

* 0.4.4

Bump release version

* Bump version number -> 0.4.5

* Added a simple menu item to print multiple part labels. This does not follow the style of the Stock label functions but it works!

* Revert "Added description as list for StockLocation"

This reverts commit f5178e9fc3.

* Added the right version number

Co-authored-by: Oliver <oliver.henry.walters@gmail.com>
2021-08-17 12:42:40 +10:00
Oliver
d6c9ff41ff Merge pull request #1964 from eeintech/import_part_nav
Added navbar on part import page
2021-08-17 09:59:35 +10:00
eeintech
99839e78fd Added navbar on part import page 2021-08-16 10:21:57 -04:00
Oliver
fa163b8866 Merge pull request #1962 from SchrodingersGat/attachment-edit
Attachment edit
2021-08-16 11:14:14 +10:00
Oliver
23b2b56de4 StockItemAttachment 2021-08-16 10:56:00 +10:00
Oliver
6141ddc3eb SalesOrderAttachment and PurchaseOrderAttachment 2021-08-16 10:53:28 +10:00
Oliver
f8b22bc7b7 Refactor BuildAttachment model 2021-08-16 10:49:31 +10:00
Oliver
d9f29b4a70 Updates for InvenTree serializer classes
- Catch and re-throw errors correctly
2021-08-16 10:41:26 +10:00
Oliver
3dcf1746e6 Functionality for renaming attached files 2021-08-16 10:41:02 +10:00
Oliver
ff8dcabb12 New custom serializer for handling attachments 2021-08-15 22:43:52 +10:00
Oliver
81a8aac623 Merge pull request #1961 from SchrodingersGat/tweaks
Small tweaks here and there
2021-08-15 22:10:09 +10:00
Oliver
1998dabe9b Small tweaks here and there 2021-08-15 21:47:37 +10:00
Oliver
4061693522 Merge pull request #1959 from SchrodingersGat/settings-context
Provide global_settings and user_settings as context objects
2021-08-15 12:57:15 +10:00
Oliver
8861ffad81 PEP fixes 2021-08-15 12:06:31 +10:00
Oliver
cef09acd54 Partial reversion of some stuff 2021-08-15 12:05:53 +10:00
Oliver
faab1f2464 Provide global_settings and user_settings as context objects
- Adds a new context middleware
- Refactor the way that settings are provided to the javascript layer
2021-08-15 11:57:05 +10:00
Oliver
6c17e330c4 Merge pull request #1958 from SchrodingersGat/better-form-errors
Better form errors
2021-08-14 13:56:33 +10:00
Oliver
28bccea57b Cleanup 2021-08-14 13:43:38 +10:00
Oliver
32fafc76d7 css tweaks 2021-08-14 13:42:50 +10:00
Oliver
f753e11f10 Improve error notification for modal forms
- Scroll to error
- Add red border and background to the form
2021-08-14 13:41:19 +10:00
Oliver
f72eb4266a remove old debug message 2021-08-14 12:31:22 +10:00
Oliver
1db654e990 Merge pull request #1956 from SchrodingersGat/supplier-part-from-form
Supplier part from form
2021-08-14 12:12:09 +10:00
Oliver
2b13512145 Check that supplier and manufacturer parts are created 2021-08-14 10:43:45 +10:00
Oliver
6fa4e33062 Unit testing for new API form features 2021-08-14 10:39:05 +10:00
Oliver
26c07961cb Bug fix for API 2021-08-14 10:23:57 +10:00
Oliver
6eb4709658 Adds initial stock quantity 2021-08-14 10:23:42 +10:00
Matthias
00d4efb920 PEP fix 2021-08-13 17:54:17 +02:00
Matthias
f0325fe30f view BOMpricing range
Closes #1889
2021-08-13 17:50:41 +02:00
Matthias
a2ffd06abf calculate purchase price for part 2021-08-13 17:49:58 +02:00
Oliver Walters
2be9399d2c CSS style fixes 2021-08-14 01:15:43 +10:00
Oliver Walters
ad844c4393 Simplify rendering of checkboxes in forms
- Display "inline" so they take up much less vertical space
2021-08-14 01:05:06 +10:00
Oliver Walters
ba1ba67f87 Only add company data if part is purchaseable 2021-08-14 00:46:30 +10:00
Oliver Walters
78340a71a9 Adds support for creation of ManufacturerPart and SupplierPart via the Part creation API 2021-08-14 00:38:08 +10:00
Oliver Walters
6218f1c7e6 Add form elements for initializing a part with supplier data 2021-08-14 00:26:22 +10:00
Oliver Walters
5cbb67b91c Add options to show / hide form groups 2021-08-14 00:20:34 +10:00
Oliver Walters
cb11df4dba Improve error checking for initial stock creation when creating a new part
- Use @transaction.atomic
- Raise proper field errors
2021-08-14 00:09:08 +10:00
Oliver Walters
1396c349c8 Refactor form field definition copying 2021-08-14 00:08:26 +10:00
Oliver Walters
5b42ab7332 Add "groups" to API forms 2021-08-13 21:48:48 +10:00
Oliver
cd4a797e71 Merge pull request #1954 from eeintech/upload_po_file
Improved creation of purchase order line items from file upload
2021-08-13 09:31:22 +10:00
Oliver
e8d47a4c76 Merge pull request #1953 from SchrodingersGat/bom-unit-tests
Add extra unit testing for BOM export
2021-08-13 09:06:29 +10:00
eeintech
9205d6d67c Improved creation of purchase order line items from file upload 2021-08-12 14:27:00 -04:00
Oliver
26ddd36666 PEP fixes 2021-08-12 23:47:42 +10:00
Oliver
537573d0e3 Add extra unit testing for BOM export 2021-08-12 23:40:07 +10:00
Oliver
1da004d30a Merge pull request #1952 from SchrodingersGat/dockerfile-fix
Dockerfile fix
2021-08-12 22:34:41 +10:00
Oliver
7df4451c08 Move some core pypi deps into requirements file 2021-08-12 22:06:47 +10:00
Oliver
a3ff90754d Specify tag when publishing release build to docker hub 2021-08-12 22:01:42 +10:00
Oliver
31bb7734ce Update dockerfile to allow downloading against a specific git tag 2021-08-12 22:01:14 +10:00
Oliver
fcff8d4825 Merge pull request #1944 from SchrodingersGat/report-unit-tests
Unit testing for report framework
2021-08-12 16:05:49 +10:00
Oliver
f59ed27cf9 Remove reliance on hard-coded PK values 2021-08-12 15:14:13 +10:00
Oliver
e36b1e6c70 PEP fixes 2021-08-12 14:51:18 +10:00
Oliver
6748f37405 Unit tests for report printing 2021-08-12 14:43:04 +10:00
Oliver
96ec8c4eb6 Copy report templates across as part of test setUp 2021-08-12 13:40:08 +10:00
Oliver
032057c93a PEP fixes 2021-08-12 13:18:10 +10:00
Oliver
7665e83001 Merge pull request #1947 from eeintech/supplier_part
Hook to connect ManufacturerPart to SupplierPart using name/MPN
2021-08-12 09:15:20 +10:00
eeintech
91e314ddb5 Only connect ManufacturerPart if a. it exists b. SupplierPart is not yet connected 2021-08-11 17:08:46 -04:00
eeintech
a2590f1a3b Merged master 2021-08-11 16:16:20 -04:00
Oliver
44818ca0c5 Some simple API unit tests 2021-08-11 17:40:00 +10:00
Oliver
3cdcdd0535 Create report templates when testing 2021-08-11 11:07:30 +10:00
Oliver
47c385cac2 Bump version number -> 0.4.5 2021-08-11 00:30:14 +10:00
Oliver
aea43924ae Merge remote-tracking branch 'inventree/master' into 0.4.x 2021-08-11 00:29:36 +10:00
Oliver
2cf7592198 Merge pull request #1941 from SchrodingersGat/lazy-loading
Adds one-shot function when a panel is displayed
2021-08-11 00:29:06 +10:00
Oliver
46fa60953d Refactor part page 2021-08-11 00:12:55 +10:00
Oliver
68282c93f4 Start API calls before the panel has finished loading 2021-08-11 00:12:48 +10:00
Oliver
a1922bff81 Adds one-shot function when a panel is displayed 2021-08-11 00:09:01 +10:00
Oliver
b94104f7ab Merge pull request #1940 from SchrodingersGat/stock-table-fix
Bug fix for stock table
2021-08-10 23:52:55 +10:00
Oliver
bfc489a35b Merge pull request #1939 from SchrodingersGat/transfer-stock-bug
Bug fix for stock transfer form
2021-08-10 23:39:31 +10:00
Oliver
535c36b75e Bug fix for stock table
- Use the provided table name, rather than hard-coded value

(cherry picked from commit b3a52dd6c65a83f2f330c0275b443e9acbcbdf73)
2021-08-10 23:31:20 +10:00
Oliver
1bf2a3e23f Merge pull request #1938 from SchrodingersGat/auth-bug
Fix bug when using token based auth
2021-08-10 23:24:01 +10:00
Oliver
4c8eaf3942 Bug fix for stock transfer form 2021-08-10 23:23:02 +10:00
Oliver
799f17ef50 Bypass custom token auth for /api/ endpoint 2021-08-10 23:09:54 +10:00
Oliver
7ef5c0058e Fix bug when using token based auth 2021-08-10 22:50:49 +10:00
Oliver
b92ec751ef Merge pull request #1934 from markdedeuge/bugfix/timezone_heartbeat
use django timezone'd datetime to squash timezone warnings from worke…
2021-08-10 15:16:42 +10:00
markdedeugeQBE
641233b140 use django timezone'd datetime to squash timezone warnings from worker heartbeat 2021-08-10 14:37:05 +10:00
Oliver
0e0f490f8d Merge pull request #1932 from SchrodingersGat/supplier-part-bug-fix
Supplier part bug fix
2021-08-10 11:18:02 +10:00
Oliver
3df7299a61 Merge pull request #1931 from markdedeuge/patch-1
Update nginx.conf to use http_host rather than host fixes #1930
2021-08-10 11:04:56 +10:00
Oliver
c32b6b2272 Hide the "part" field when editing manufacturer part and supplier part objects 2021-08-10 11:01:17 +10:00
Oliver
e03afce335 Add "instance_filters" for SupplierPart model
- Restrict the "manufacturer_part" query based on the current part
2021-08-10 11:00:27 +10:00
Oliver
7117c33379 Raise validation error if the manufacturer part does not point to the correct part 2021-08-10 10:58:11 +10:00
Mark De Deuge
fc06bc7574 Update nginx.conf to use http_host rather than host fixes #1930
Using proxy_set_header Host $host; does not pass through the port. This causes the /api-doc/ route to miss the port when attempting to render routes. 
This fixes: #1930
2021-08-10 10:27:40 +10:00
Oliver
6620d34f25 Merge pull request #1913 from eeintech/bom_upload
BOM upload templates fixes
2021-08-10 09:11:25 +10:00
Oliver
0a1ce59dfc Merge pull request #1858 from eeintech/exchange_rate_task
Exchange rate as worker task
2021-08-10 08:23:46 +10:00
eeintech
69d1c3cea2 Improved task import to support global 2021-08-09 11:55:56 -04:00
eeintech
372d252333 Merge branch 'master' of github.com:inventree/InvenTree into exchange_rate_task 2021-08-09 10:47:35 -04:00
Oliver
98211f1ae1 Merge pull request #1928 from erkutalakus/hotfix/unlocalization-of-item-quantity
Localization of item.quantity removed
2021-08-09 21:42:07 +10:00
Erkut Alakuş
c564896355 Localization of item.quantity removed
Localization of quantity for different cultures(turkish in my case) using comma(,) instead of dot(.) leads syntax error in javascript code and prevents stock item history to load.
2021-08-09 12:11:53 +03:00
Oliver
50198c0f1e Merge remote-tracking branch 'inventree/master' into 0.4.x 2021-08-09 16:19:47 +10:00
Oliver
970d2ac1f8 Merge pull request #1926 from SchrodingersGat/api-bump
Bump API version to from 8 -> 9
2021-08-09 16:19:26 +10:00
Oliver
bb8b9dfcec Bump API version to from 8 -> 9
(Also moved the API version info from version.py to
2021-08-09 14:54:07 +10:00
Oliver
90d753001f Merge pull request #1925 from matmair/fix-price-breaksfilters
Fix for missing part filter
2021-08-09 14:49:49 +10:00
Matthias
ad656b7ca7 extending API to supply price wihtout formatting 2021-08-09 01:49:55 +02:00
Oliver
a846334698 0.4.4
Bump release version
2021-08-09 09:45:56 +10:00
Oliver
e8d4e2a7e6 Merge remote-tracking branch 'inventree/master' into 0.4.x 2021-08-09 09:45:13 +10:00
Oliver
9565b3004d L10 (#1924)
* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2021-08-09 09:44:32 +10:00
Matthias
c58ed5a07e Fixes #1915 2021-08-09 01:43:50 +02:00
Oliver
681f285aa3 Merge pull request #1923 from SchrodingersGat/secondary-modals
Secondary modals
2021-08-08 01:13:42 +10:00
Oliver Walters
e3efd12184 secondary for purcahse order 2021-08-08 00:50:59 +10:00
Oliver Walters
52ba2201b2 secondary for creating sales order customers 2021-08-08 00:49:16 +10:00
Oliver Walters
4e6c8c45ee secondary modal for part form 2021-08-08 00:44:46 +10:00
Oliver Walters
1dc5682767 bug fix 2021-08-08 00:44:30 +10:00
Oliver
0ad206d9eb Merge pull request #1922 from SchrodingersGat/secondary-modals
Secondary modals
2021-08-08 00:06:45 +10:00
Oliver Walters
720b332f71 Adds secondary to manufacturerpart form 2021-08-07 23:49:50 +10:00
Oliver Walters
94c1ed882f Add secondary field options for "Create Supplier Part" form
- Supplier
- Manufacturer Part
2021-08-07 23:42:42 +10:00
Oliver Walters
b960ce839a remove debug statement 2021-08-07 23:31:21 +10:00
Oliver Walters
fd917b2e41 Simple refactoring 2021-08-07 23:30:53 +10:00
Oliver Walters
25af8559ba Back-fill newly created model data into the parent modal form 2021-08-07 23:27:31 +10:00
Oliver Walters
759a3724b5 Add function to extract all data from a displayed form 2021-08-07 23:09:56 +10:00
Oliver
0d80ae9f93 Merge pull request #1921 from SchrodingersGat/label-print-options
Add inline option for reports, too
2021-08-07 22:49:55 +10:00
Oliver Walters
e583d1a159 Actually use the variable, I suppose 2021-08-07 22:30:51 +10:00
Oliver Walters
f405e4b701 Add inline option for reports, too 2021-08-07 22:28:48 +10:00
Oliver
5448165ef3 Merge pull request #1920 from SchrodingersGat/label-print-options
Label print options
2021-08-07 22:27:34 +10:00
Oliver Walters
ca9536f687 Implement option to display labels "inline" in the browser 2021-08-07 22:09:15 +10:00
Oliver
8e8696eda9 Merge pull request #1919 from SchrodingersGat/template-name-fix-2
Add model validator to prevent illegal names for PartParameterTemplate
2021-08-07 22:00:11 +10:00
Oliver Walters
d77b99c0ca Add user settings for report and labels 2021-08-07 21:57:41 +10:00
Oliver Walters
174ac64235 Allow downloaded files to be inline or attachments 2021-08-07 21:45:18 +10:00
Oliver Walters
24638a7229 Add migration file 2021-08-07 21:41:45 +10:00
Oliver
7629077deb Merge pull request #1918 from SchrodingersGat/version-number-check
Check release tag before publishing docker image
2021-08-07 21:35:03 +10:00
Oliver Walters
6d42cfab75 Add model validator to prevent illegal names for PartParameterTemplate
(cherry picked from commit 93805a87e0)
2021-08-07 21:23:52 +10:00
Oliver Walters
e72e34413d Check release tag before publishing docker image 2021-08-07 21:21:09 +10:00
Oliver
512eceb2a6 Merge pull request #1916 from SchrodingersGat/part-params-fix
Fix part settings page
2021-08-07 21:07:30 +10:00
Oliver Walters
2e5d5bcc84 Fix part settings page 2021-08-07 20:50:32 +10:00
Oliver
6234581fab Update README.md 2021-08-07 11:16:37 +10:00
eeintech
6280ed1ade Moved enableNavbar to part_base template 2021-08-06 11:15:16 -04:00
eeintech
1eb8a9f310 BOM upload templates fixes 2021-08-05 17:39:08 -04:00
eeintech
1bf49a1458 Re-added ability to link manufacturer part to supplier part via API 2021-08-05 14:06:31 -04:00
Oliver
ce62da5a42 Merge remote-tracking branch 'inventree/master' into 0.4.x 2021-08-05 23:35:34 +10:00
Oliver
76572bf82f Merge pull request #1907 from matmair/trans-improv
translation improvement
2021-08-05 20:28:45 +10:00
Matthias Mair
96378cb556 Merge branch 'inventree:master' into trans-improv 2021-08-05 08:17:21 +02:00
Oliver
599c53ea53 Merge remote-tracking branch 'inventree/master' into 0.4.x 2021-08-05 13:24:47 +10:00
Oliver
a45437dac8 Merge pull request #1909 from SchrodingersGat/new-weasyprint
Pin weasyprint version to 52.5
2021-08-05 13:24:27 +10:00
Oliver
00ffab472c Fix for build report template 2021-08-05 10:44:47 +10:00
Oliver
fa6daeb679 Pin weasyprint version to 52.5 2021-08-05 10:30:38 +10:00
Matthias
d6c6cb96ba make keyvalue non-existing key tolerant 2021-08-05 01:24:49 +02:00
Matthias
58f2dce18d show translation level in ui 2021-08-05 01:23:11 +02:00
Matthias
d6672372a5 script to save the locale stats 2021-08-05 00:44:02 +02:00
Oliver
96b5f70c21 Merge remote-tracking branch 'inventree/master' into 0.4.x 2021-08-05 08:43:04 +10:00
Oliver
be67832821 Merge pull request #1908 from eeintech/fix_typo
Fixed typo for build responsible column header
2021-08-05 08:14:51 +10:00
Oliver
38815211af Merge pull request #1904 from SchrodingersGat/part-forms
Refactor Part creation and editing forms
2021-08-05 08:14:32 +10:00
eeintech
c0ccb8f588 Fixed typo for build responsible column header 2021-08-04 17:11:35 -04:00
Matthias
3ecb1e6577 cleaner structure 2021-08-04 19:44:01 +02:00
eeintech
fbdf11e6e7 Merged run_task code into offload_task function
Added option to force synchronous operation
Use that option for update_exchange_rates
2021-08-04 11:23:52 -04:00
Oliver Walters
c7712d4235 even more unit tests 2021-08-05 01:13:48 +10:00
Oliver Walters
655e5692e9 More unit test fixes 2021-08-05 00:58:07 +10:00
Oliver Walters
6acff2a26e Fixes unit test 2021-08-05 00:40:02 +10:00
Oliver Walters
aaf394ca7a PEP fixes 2021-08-05 00:26:21 +10:00
Oliver Walters
dd78464a74 remove unused function 2021-08-05 00:25:47 +10:00
Oliver Walters
aa4ed9feb0 Refactor MakeVariant form
- Now is essentially identical to the DuplicatePart form
- Uses the API form structure
2021-08-05 00:24:38 +10:00
eeintech
1b79ef940e Merge branch 'master' of github.com:inventree/InvenTree into exchange_rate_task 2021-08-04 10:22:14 -04:00
Oliver Walters
0e8fb6a5ad Refactored DuplicatePart form
- API endpoint now takes care of duplication of other data
2021-08-05 00:16:42 +10:00
Oliver Walters
2cb0b448b7 Fix error message styles for API errors
- django ValidationError uses "__all__" key for non_field_errors
- whyyyyyyyyyyyy
2021-08-05 00:15:55 +10:00
Oliver Walters
408ff639dd Adds ability to pre-fill a form with a complete dataset 2021-08-04 23:48:21 +10:00
Oliver Walters
1fafaf8577 Refactor partfields function (was essentially duplicated) 2021-08-04 23:29:39 +10:00
Oliver Walters
b04f22fc53 CreatePart form now uses the API
- Simplify the way category parameter templates are copied
2021-08-04 23:27:16 +10:00
Oliver Walters
2bf3e3ab02 Function to construct part form fields 2021-08-04 23:26:17 +10:00
Oliver Walters
a64ee23afc Add more options for form rendering
- "before" a field
- "after" a field
- pure "eye candy" field
2021-08-04 23:16:11 +10:00
Oliver
5aa111b0aa Merge pull request #1902 from SchrodingersGat/bom-item-form
Use API forms for creating and editing BomItem objects
2021-08-04 18:06:42 +10:00
Oliver Walters
2e8a490ca9 Fixes for unit tests 2021-08-04 17:41:47 +10:00
Oliver Walters
75a1be0284 Use API forms for creating and editing BomItem objects 2021-08-04 17:25:51 +10:00
Matthias
efd4644045 translation information 2021-08-04 07:55:58 +02:00
Oliver
96a79e557b Merge pull request #1901 from SchrodingersGat/table-refresh
Adds a button to tables to reload data
2021-08-04 14:59:51 +10:00
Oliver
1f70538b04 Adds a button to tables to reload data 2021-08-04 14:24:17 +10:00
Oliver
db6d7c2d27 Merge remote-tracking branch 'inventree/master' into 0.4.x 2021-08-04 12:32:36 +10:00
Oliver
7911801dbf Merge pull request #1900 from SchrodingersGat/part-image-search
Make the part thumbnail selection window searchable
2021-08-04 12:28:52 +10:00
Oliver
56c0e289bd Style fix 2021-08-04 12:13:24 +10:00
Oliver
6cd87e830d Merge remote-tracking branch 'inventree/master' into 0.4.x 2021-08-04 12:12:12 +10:00
Oliver
f95346f214 Make the part thumbnail selection window searchable 2021-08-04 12:10:49 +10:00
Oliver
7545591c59 Merge pull request #1899 from SchrodingersGat/cat-create
Refactor "CreatePartCategory" form to API
2021-08-04 11:52:21 +10:00
Oliver
989983bdb5 Fixed missing import 2021-08-04 11:37:59 +10:00
Oliver
83d8226ad6 Refactor "CreatePartCategory" form to API
(cherry picked from commit 06ff961564)
2021-08-04 11:34:42 +10:00
Oliver
c4570a79de Merge remote-tracking branch 'inventree/master' into 0.4.x 2021-08-04 09:04:24 +10:00
Oliver
8daf601f00 Merge pull request #1897 from matmair/price-terms
updating language to be clearer
2021-08-04 09:03:13 +10:00
Oliver
69f242d11d Merge pull request #1892 from eeintech/stock_installed_items
Re-enabled installing stock items into others
2021-08-04 09:01:28 +10:00
Oliver
910c57c92d Merge pull request #1895 from eeintech/fix_has_ipn_filter
'has_ipn' filter method did not return queryset
2021-08-04 08:55:49 +10:00
Matthias
fa3c5ae108 updating language to be clearer
see https://github.com/inventree/InvenTree/issues/1889#issuecomment-891901070
2021-08-04 00:45:56 +02:00
eeintech
29c8daed0a 'has_ipn' filter method did not return queryset 2021-08-03 12:21:44 -04:00
eeintech
172a08fbba Removed old quantity setting lines 2021-08-03 09:53:08 -04:00
Oliver
073bb7c488 Merge pull request #1894 from SchrodingersGat/non-int-serial-fix
Fix for non-integer serial numbers

(cherry picked from commit 529742b520)
2021-08-03 10:06:19 +10:00
Oliver
529742b520 Merge pull request #1894 from SchrodingersGat/non-int-serial-fix
Fix for non-integer serial numbers
2021-08-03 10:03:05 +10:00
Oliver
f057937df0 Fix for non-integer serial numbers 2021-08-03 09:46:28 +10:00
eeintech
1c4924a4a5 Style duh 2021-08-02 15:14:55 -04:00
eeintech
ac3dcac641 Re-enabled installing stock items into others 2021-08-02 15:05:24 -04:00
Oliver
9aa5086067 Merge pull request #1819 from eeintech/part_main_details
Cleaner part details
2021-08-02 08:47:18 +10:00
Oliver
b18f360daf 0.4.2 2021-08-02 08:43:04 +10:00
Oliver
20cc952982 Merge pull request #1887 from matmair/settings-safety
settings fixes

(cherry picked from commit d154ca08ea)
2021-08-02 08:42:34 +10:00
Oliver
cd39fd1dc2 Merge pull request #1890 from matmair/fix-for-1888
catch connection errors in exchange update

(cherry picked from commit db57e9516b)
2021-08-02 08:42:26 +10:00
Oliver
d154ca08ea Merge pull request #1887 from matmair/settings-safety
settings fixes
2021-08-02 08:38:34 +10:00
Oliver
db57e9516b Merge pull request #1890 from matmair/fix-for-1888
catch connection errors in exchange update
2021-08-02 08:34:52 +10:00
Matthias
0f11ab527f PEP fix 2021-08-01 20:58:57 +02:00
Matthias
83dab558d7 catch connection errors in exchange update
#Fixes #1888
2021-08-01 20:44:26 +02:00
Matthias
55762f2a96 do not use safe in template
that can cause wrong escaping and generally is considered unsafe
2021-08-01 01:41:46 +02:00
Matthias
c0921fc7ce removing unneeded prints 2021-08-01 01:16:10 +02:00
Matthias
ae8e58ac12 invoke task for celan_settings 2021-08-01 01:06:17 +02:00
Matthias
2347f15c2e new command to cleanup old settings in db 2021-08-01 01:05:43 +02:00
Matthias
369864574e only include setting in the settings that have a key 2021-07-30 23:25:45 +02:00
eeintech
634e5e0da6 Added toggle for part details
Added persistence for page refresh or new part page
2021-07-30 14:55:12 -04:00
eeintech
20b21a2b71 Merge branch 'master' of github.com:inventree/InvenTree into part_main_details 2021-07-30 10:50:28 -04:00
Oliver
7ed43f5a11 Merge pull request #1886 from inventree/revert-1885-docker-publish
Revert "Publish on tags also"
2021-07-30 13:39:49 +10:00
Oliver
1d19393442 Revert "Publish on tags also" 2021-07-30 12:42:03 +10:00
Oliver
6d0b01d0fb Merge pull request #1885 from SchrodingersGat/docker-publish
Publish on tags also
2021-07-30 12:33:31 +10:00
Oliver
13898d6687 Publish on tags also 2021-07-30 11:51:06 +10:00
Oliver
0e59c15773 0.4.1 2021-07-30 11:26:53 +10:00
Oliver
eb883d7e70 Merge pull request #1884 from SchrodingersGat/typo-fix
logging.get -> logging.getLogger
2021-07-30 00:09:43 +10:00
Oliver Walters
d9f4c34a42 logging.get -> logging.getLogger 2021-07-29 23:44:52 +10:00
Oliver
3806e3ebeb Merge pull request #1880 from SchrodingersGat/settings-fix
Bug fix
2021-07-29 17:56:45 +10:00
Oliver
c39e3aaa82 Bug fix 2021-07-29 17:52:24 +10:00
Oliver
60e4022568 Merge pull request #1879 from SchrodingersGat/docker-improvements
Specify how many workers to use
2021-07-29 17:19:41 +10:00
Oliver
542c204ca0 PEP fixes 2021-07-29 16:39:51 +10:00
Oliver
dd12a593f4 Specify how many workers to use 2021-07-29 16:37:34 +10:00
Oliver
935ef968de Merge pull request #1878 from SchrodingersGat/js-template-stuff
Js template stuff
2021-07-29 13:42:47 +10:00
Oliver
7756c766c3 Fix stock.js 2021-07-29 12:35:21 +10:00
Oliver
4381a16b0e Template cleanup 2021-07-29 12:31:07 +10:00
Oliver
6fe5f0e0e6 Fixes for order.js 2021-07-29 11:58:32 +10:00
Oliver
ba5479090a Fix nav.js 2021-07-29 11:54:04 +10:00
Oliver
28bf5bfdbc Fix table_filters.js 2021-07-29 11:52:50 +10:00
Oliver
a222efda33 Fix rendering issues 2021-07-29 11:43:50 +10:00
Oliver
27ec65a002 Add 'settings.js' which provides all settings (global and user) as a dynamic javascript file
- Minimal database hits required
2021-07-29 11:28:04 +10:00
Oliver
915756eacf Improve test output 2021-07-29 09:28:08 +10:00
Oliver
8e97d14f1f Rename CI test 2021-07-29 09:26:56 +10:00
Oliver
14aebfdae1 Split dynamic javascript files into two separate directories
- One gets translated and is served statically
- One does not get translated and is served dynamically
- Add CI step
2021-07-29 09:23:24 +10:00
Oliver
bc3c3be751 force linux-style line endings for .sh files 2021-07-29 09:10:46 +10:00
Oliver
0a73032950 Merge pull request #1877 from eeintech/fix_search_js
Fixed missing comma propagating to translated JS files

(cherry picked from commit 2009773d9d)
2021-07-29 08:27:49 +10:00
Oliver
2009773d9d Merge pull request #1877 from eeintech/fix_search_js
Fixed missing comma propagating to translated JS files
2021-07-29 08:27:20 +10:00
eeintech
d43312d162 Missing comma propagating to translated JS files 2021-07-28 13:29:12 -04:00
Oliver
430f737953 Merge pull request #1876 from SchrodingersGat/mpn-api
Adds an API filter class for the ManufacturerPart list endpoint
2021-07-29 00:23:44 +10:00
Oliver
baa6283d20 Fixes 2021-07-28 23:47:50 +10:00
Oliver
5744796506 Adds an API filter class for the ManufacturerPart list endpoint 2021-07-28 23:32:49 +10:00
Oliver
a7229b5b0b Merge pull request #1874 from SchrodingersGat/docker-dev-fix
Copy static files when starting dev server

(cherry picked from commit 50eb70f538)
2021-07-28 22:50:31 +10:00
Oliver
50eb70f538 Merge pull request #1874 from SchrodingersGat/docker-dev-fix
Copy static files when starting dev server
2021-07-28 22:50:03 +10:00
Oliver
399e44fce7 Copy static files when starting dev server 2021-07-28 22:30:41 +10:00
Oliver
20b6e0fd1a Update version.py 2021-07-28 15:46:52 +10:00
Oliver
9d9bfd6c30 Update version.py
0.4.0 release
2021-07-28 15:46:20 +10:00
Oliver
5dc11ad8e9 Merge pull request #1873 from SchrodingersGat/translation
Translation
2021-07-28 15:45:08 +10:00
Oliver
839cd55f20 Merge branch 'l10' into translation
# Conflicts:
#	InvenTree/locale/de/LC_MESSAGES/django.po
#	InvenTree/locale/el/LC_MESSAGES/django.po
#	InvenTree/locale/en/LC_MESSAGES/django.po
#	InvenTree/locale/es/LC_MESSAGES/django.po
#	InvenTree/locale/fr/LC_MESSAGES/django.po
#	InvenTree/locale/he/LC_MESSAGES/django.po
#	InvenTree/locale/id/LC_MESSAGES/django.po
#	InvenTree/locale/it/LC_MESSAGES/django.po
#	InvenTree/locale/ja/LC_MESSAGES/django.po
#	InvenTree/locale/ko/LC_MESSAGES/django.po
#	InvenTree/locale/nl/LC_MESSAGES/django.po
#	InvenTree/locale/no/LC_MESSAGES/django.po
#	InvenTree/locale/pl/LC_MESSAGES/django.po
#	InvenTree/locale/ru/LC_MESSAGES/django.po
#	InvenTree/locale/sv/LC_MESSAGES/django.po
#	InvenTree/locale/th/LC_MESSAGES/django.po
#	InvenTree/locale/tr/LC_MESSAGES/django.po
#	InvenTree/locale/vi/LC_MESSAGES/django.po
#	InvenTree/locale/zh/LC_MESSAGES/django.po
2021-07-28 15:23:10 +10:00
Oliver
78ed2776d1 Merge pull request #1863 from SchrodingersGat/settings-refactor
Refactoring existing settings views
2021-07-28 15:04:37 +10:00
Oliver
6fd70e4741 Fix search settings page 2021-07-28 14:18:42 +10:00
Oliver
cd1ecc5e8f style fixes 2021-07-28 14:17:25 +10:00
Oliver
1fe00ef7df Refactor 'category' setttings 2021-07-28 14:16:55 +10:00
Oliver
25ff74835d Refactor PartCategoryParameter API 2021-07-28 14:05:49 +10:00
Oliver
29b496b588 Merge pull request #1870 from eeintech/fix_1864
Fix user setting query
2021-07-28 07:01:46 +10:00
Oliver
d6420341f4 Merge pull request #1871 from eeintech/stock_item_remove_redirect
Redirect to stock index if stock item is completely depleted
2021-07-28 07:00:47 +10:00
eeintech
e9be3fb4ec Redirect to stock index if stock item is completely depleted 2021-07-27 11:11:30 -04:00
eeintech
4e3635d1fe Exact query on ID made the PostGreSQL DB choke 2021-07-27 10:05:22 -04:00
Oliver
c08cb43c39 Template rendering fix for inventree.js 2021-07-27 17:22:44 +10:00
Oliver
a93d96de58 Cleanjup 2021-07-27 17:13:02 +10:00
Oliver
990b987692 Reload settings values "live" (without a page refresh) 2021-07-27 17:03:37 +10:00
Oliver
46d5a6f00b - Convert "PART_RECENT_COUNT" to user setting
- Convert "STOCK_RECENT_COUNT" to user setting
2021-07-27 14:53:32 +10:00
Oliver
33a0b73a05 Convert SEARCH_PREVIEW_RESULTS to a "user" setting 2021-07-27 14:44:51 +10:00
Oliver
3e0655a9d8 Add "homepage" settings page 2021-07-27 14:34:23 +10:00
Oliver
7fdc0546b4 Merge remote-tracking branch 'inventree/master' into settings-refactor 2021-07-27 14:28:17 +10:00
Oliver
985870d626 Translation merge (#1869)
* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2021-07-27 13:11:46 +10:00
Oliver
fff4f27816 Merge pull request #1868 from SchrodingersGat/new-locales
Support more language codes
2021-07-27 09:48:02 +10:00
Oliver
461238f8ae Locale name fix 2021-07-27 08:57:33 +10:00
Oliver
783f43db5f Support more language codes 2021-07-27 08:53:50 +10:00
Oliver
cfd4626c96 More PEP cleanin 2021-07-27 08:33:41 +10:00
Oliver
35d64b7069 PEP cleanup 2021-07-27 08:33:22 +10:00
Oliver
de2d9b30ec Merge pull request #1867 from SchrodingersGat/delete-fix
inventreeDelete was not returning the promise
2021-07-27 08:32:04 +10:00
Oliver
c63a061cf3 Refactor "theme" selection 2021-07-27 08:31:43 +10:00
Oliver
57551d3ac3 inventreeDelete was not returning the promise 2021-07-27 08:17:53 +10:00
Oliver
0186d23563 inline theme form 2021-07-27 07:56:24 +10:00
Oliver
f9918758dd Merge pull request #1866 from eeintech/fix_1861
Fixes BOM purchase price conversion
2021-07-27 07:35:31 +10:00
eeintech
a6b52a9fae Fix for #1861 2021-07-26 17:02:04 -04:00
eeintech
f61c768bbe Merge branch 'master' of github.com:inventree/InvenTree into part_main_details 2021-07-26 13:59:15 -04:00
Oliver
3349e9ff6c Refactor language selection 2021-07-27 00:58:31 +10:00
Oliver
b7b0574a44 Add "appearance" 2021-07-27 00:38:59 +10:00
Oliver
9b0fbb7006 Separate tab for "barcode" settings 2021-07-27 00:36:41 +10:00
Oliver
ca1c692b15 Refactor "Currency settings" view 2021-07-27 00:29:55 +10:00
Oliver
de89c3997d Refactoring a lot of existing settings views 2021-07-27 00:20:31 +10:00
Oliver
125260160c Merge pull request #1859 from matmair/homepage-settings
User settings
2021-07-26 23:19:05 +10:00
Matthias
aabefc2815 Merge branch 'master' of https://github.com/inventree/InvenTree into homepage-settings 2021-07-25 00:09:34 +02:00
Oliver
bcfb3ac067 Merge pull request #1860 from matmair/fix-for-1775
fix for phantom migration warning
2021-07-24 10:07:07 +10:00
Matthias
4a32bdb7ab fix for phantom migration warning
Closes #1775
2021-07-24 01:30:53 +02:00
Matthias
175b24a794 changing user settings icon 2021-07-24 00:45:37 +02:00
Matthias
a82483dbaa hiding homepage block when no setting is used 2021-07-24 00:44:16 +02:00
Matthias
431b35ed32 new tag for building lists 2021-07-24 00:42:17 +02:00
Matthias
31050f23aa adding all homepage settings 2021-07-24 00:08:46 +02:00
Matthias
32eace0c36 moving settings into own section 2021-07-23 11:05:41 +02:00
Matthias
e97ee95deb typo 2021-07-23 01:09:06 +02:00
Matthias
46b0db8263 more hompage settings 2021-07-23 01:07:28 +02:00
Matthias
e167f27258 get user settings in templates 2021-07-23 00:46:48 +02:00
Matthias
50356afd41 Merge branch 'master' of https://github.com/inventree/InvenTree into homepage-settings 2021-07-23 00:03:21 +02:00
Matthias
7abf70fdd7 style fix 2021-07-22 23:53:17 +02:00
Matthias
3b12b0231e fixing wired unique behaviour 2021-07-22 23:50:51 +02:00
Matthias
5f2bef7ee1 base implementation of user setting 2021-07-22 23:50:09 +02:00
Matthias
3f6c7df7a8 change template setting behaviour for user setting 2021-07-22 23:48:28 +02:00
Matthias
e287860e10 admin for user setting 2021-07-22 23:46:31 +02:00
Matthias
449fc329c9 usersetting edit url 2021-07-22 23:45:34 +02:00
Matthias
7ef87320a0 abstract edit 2021-07-22 23:44:25 +02:00
Matthias
6f5fc528b7 override functions 2021-07-22 23:43:51 +02:00
Matthias
8f374e255e abstract filters and refactor 2021-07-22 23:43:03 +02:00
Matthias
69ff0ac248 ruleset 2021-07-22 23:39:36 +02:00
Matthias
c0d6ef80fc unique model settings 2021-07-22 23:39:19 +02:00
eeintech
3f44233074 Improve import 2021-07-22 16:41:45 -04:00
eeintech
d7028b6d74 Make it generic method instead 2021-07-22 16:34:35 -04:00
eeintech
9b542ed23f Run exchange rate updated as task if worker cluster is running 2021-07-22 15:55:17 -04:00
Oliver
2bf7b61668 Merge pull request #1857 from SchrodingersGat/part-parameters-in-reports
Part parameters in reports
2021-07-21 22:33:39 +10:00
Oliver
964672d6cc Add parameters to template 2021-07-21 22:14:03 +10:00
Oliver
afde997cf9 Expose part parameters to Part label templates 2021-07-21 22:05:52 +10:00
Oliver
20a30f317f Merge pull request #1856 from SchrodingersGat/query-filters
Add instance-specific filters to API OPTIONS data
2021-07-21 21:50:17 +10:00
Oliver
4ee0004c97 Filtering for Build and StockItem 2021-07-21 21:34:16 +10:00
Oliver
9cf372f633 PEP fixes 2021-07-21 21:24:18 +10:00
Oliver
85a40ec418 Tree exclusion for PartCategory and StockLocation 2021-07-21 21:23:30 +10:00
Oliver
bee0a519ef Allow filtering of PartList by exclude_tree 2021-07-21 21:18:01 +10:00
Oliver
df48df8119 Catch recursive tree error for part / variant relationship 2021-07-21 21:10:31 +10:00
Oliver
dad9239a1c Add instance-specific filters to API OPTIONS data 2021-07-21 20:59:55 +10:00
Oliver
5f8c9a0f31 Merge pull request #1855 from SchrodingersGat/search-preview-results
Add an option to configure number of parts in search preview
2021-07-21 15:26:27 +10:00
Oliver
2ffae368f1 Add an option to configure number of parts displayed in search preview box 2021-07-21 15:05:14 +10:00
Oliver
140396ccdd Merge pull request #1854 from SchrodingersGat/url-unit-test
Add unit test for validation of reverse url lookup
2021-07-21 11:09:10 +10:00
Oliver
15b3055e53 Merge pull request #1838 from matmair/search-autocomplete
Search add autocomplete
2021-07-21 10:55:40 +10:00
Oliver
893628d1b8 URL fixes 2021-07-21 10:52:14 +10:00
Oliver
8cb336f581 PEP fixes 2021-07-21 10:42:24 +10:00
Oliver
2d6a78ffb8 Add unit test for validation of reverse url lookup 2021-07-21 10:25:16 +10:00
Oliver
8eeb88a0ea Merge pull request #1850 from SchrodingersGat/api-speed
Api speed
2021-07-21 09:42:31 +10:00
Oliver
598ea11211 Add manager class for StockItem 2021-07-21 09:28:58 +10:00
Oliver
96a065cdb7 Merge pull request #1852 from eeintech/po_destination
Improved handling of PO items destination
2021-07-21 08:09:39 +10:00
eeintech
8ac3d42fd8 Improved handling of po items destination 2021-07-20 17:15:01 -04:00
Matthias
ff07cf5516 cleanup 2021-07-20 18:52:52 +02:00
Matthias
30f94bef41 adding style and picture 2021-07-20 18:51:27 +02:00
Oliver
5e2145e151 Bug fix - delete line which don't belong no more 2021-07-20 22:26:43 +10:00
Oliver
b04a403081 subclass TreeManager 2021-07-20 22:15:49 +10:00
Oliver
cb0b7209ec Add custom "list" function back in
- Actually does make a significant difference to query speed
2021-07-20 22:12:01 +10:00
Oliver
4199e7567f Remove duplicate annotation call 2021-07-20 21:46:27 +10:00
Oliver
dbe550a159 Optimizations for PartList API endpoint:
- Remove custom list() function
- Queryset prefetch now performed by the model manager
2021-07-20 21:37:32 +10:00
Oliver
84fc2785d6 Create a custom Manager class for the Part model
- Always perform prefetch_related calls
2021-07-20 21:26:51 +10:00
Matthias
d0e425ad30 Merge branch 'search-autocomplete' of https://github.com/matmair/InvenTree into search-autocomplete 2021-07-20 07:49:42 +02:00
Matthias
289b030f4e limit results in response 2021-07-20 07:49:21 +02:00
Oliver
d9673244d5 Merge pull request #1849 from SchrodingersGat/url-fix
Fix URL patterns for ManufacturerPart and SupplierPart
2021-07-20 15:21:32 +10:00
Oliver
b0c4a58f30 Fix URL patterns for ManufacturerPart and SupplierPart 2021-07-20 15:06:09 +10:00
Oliver
4600b0d337 Update README.md 2021-07-20 11:35:55 +10:00
Matthias
ce3f7b698d InvenTreeUserSettings added 2021-07-20 01:35:58 +02:00
Matthias
ec53099872 abstracting Settings model 2021-07-20 01:34:35 +02:00
Oliver
17c8dc3441 Merge pull request #1844 from eeintech/po_import
Purchase Order File Upload Update
2021-07-20 07:58:48 +10:00
Oliver
8d04acd5f5 Merge pull request #1846 from matmair/bom-internal
Allow BOM pricing to be valid when using internal pricing - style fix
2021-07-20 07:53:43 +10:00
Matthias Mair
ebb202b19b Merge branch 'inventree:master' into search-autocomplete 2021-07-19 23:39:16 +02:00
eeintech
456710c5ce clean_decimal should also check if the string can be converted to Decimal type 2021-07-19 15:57:51 -04:00
Matthias Mair
c2ce569506 Merge branch 'inventree:master' into bom-internal 2021-07-19 21:51:18 +02:00
eeintech
53f2aa107a Umm watch out for the true fix! 2021-07-19 15:51:04 -04:00
Matthias
8d2e910323 style fix 2021-07-19 21:50:06 +02:00
eeintech
9acd57f8e0 CI was not completely fixed 2021-07-19 15:29:04 -04:00
eeintech
c1db4c7b3d Try to catch encoding error, fixed CI 2021-07-19 15:14:08 -04:00
eeintech
3ab058e84b Fixed default currency selection 2021-07-19 14:49:55 -04:00
eeintech
23db7a89a9 Updated PO upload template, moved call to button, improved cleaned_decimal method to handle comma separator 2021-07-19 14:20:54 -04:00
eeintech
2703ae520e Merge branch 'master' of github.com:inventree/InvenTree into part_main_details 2021-07-19 09:20:06 -04:00
Oliver
af68ea23c3 Merge pull request #1843 from SchrodingersGat/stock-export
Stock export
2021-07-19 17:45:11 +10:00
Oliver
ca36775f62 Merge pull request #1842 from SchrodingersGat/installed-items
Add "installed_items" as a context variable to TestReport
2021-07-19 17:23:56 +10:00
Oliver
4413699844 PEP fixes 2021-07-19 17:23:51 +10:00
Oliver
efb4f194b6 Refactor StockExportOptions form 2021-07-19 17:23:18 +10:00
Oliver
87d4a51575 Add "installed items" section to default TestReport 2021-07-19 16:44:56 +10:00
Oliver
4fdb18318a Add "installed_items" as a context variable to the StockItem TestReport template 2021-07-19 16:32:36 +10:00
Matthias
1d549bcdbd style fix 2021-07-19 07:56:55 +02:00
Oliver
e7d9485c16 Merge pull request #1841 from SchrodingersGat/api-bump
0.3.1
2021-07-19 15:56:39 +10:00
Matthias
43f1e2b8f9 switching back to using extensive apis for search 2021-07-19 07:54:12 +02:00
Oliver
e04bbb016d 0.3.1
Bumped API version to 8
2021-07-19 15:10:57 +10:00
Oliver
44d45050b9 Merge pull request #1840 from SchrodingersGat/bom-table-fix
Fix duplicate table naming
2021-07-19 15:03:31 +10:00
Oliver
40c203c123 Ensure BOM pricing table is loaded 2021-07-19 14:40:02 +10:00
Oliver
bbada3e873 Fix duplicate table naming 2021-07-19 14:34:58 +10:00
Oliver
fa9ef02d23 Merge pull request #1836 from SchrodingersGat/supplier-part-forms
Supplier part forms
2021-07-19 11:17:45 +10:00
Oliver
33e70ec5a7 Unit test fixes 2021-07-19 10:55:23 +10:00
Oliver
efd0caba6e Merge pull request #1829 from matmair/initial-amount
Initial amount
2021-07-19 09:53:40 +10:00
Oliver
565fe9a98e Remove custom creation code for SupplierPart serializer 2021-07-19 09:44:48 +10:00
Matthias
4013abe4de style fix 2021-07-19 01:26:26 +02:00
Matthias
6ca34276bd refactor 2021-07-19 01:17:32 +02:00
Matthias
866c8af393 adding styling to autocomplete 2021-07-19 01:07:33 +02:00
Matthias
17eee66b95 Adding search auto-complete #280 2021-07-19 00:46:51 +02:00
Matthias
c71fbf7893 added autocomplete to jquery ui 2021-07-18 20:57:15 +02:00
Oliver Walters
0d660e3c69 Unit test fixes
(cherry picked from commit 787064abc0)
2021-07-19 00:24:21 +10:00
Oliver Walters
0288a1acbf Refactor edit and delete views for ManufacturerPart 2021-07-18 22:59:34 +10:00
Oliver Walters
0c91691ed2 Refactor SupplierPartEdit and SupplierPartDelete forms 2021-07-18 22:46:23 +10:00
Oliver Walters
29d7cb40e1 Add edit and delete buttons for supplier-part table 2021-07-18 22:31:04 +10:00
Oliver Walters
56fa6c512b Refactor SupplierPartCreate form 2021-07-18 22:21:11 +10:00
Oliver
f9b7257bdb Merge pull request #1835 from SchrodingersGat/disable-secondary
Disable secondary modals
2021-07-18 21:46:22 +10:00
Oliver
14e2cabffa Merge pull request #1834 from SchrodingersGat/add-line-item-fix
Add 'destination' field to POLineItem API serializer
2021-07-18 21:41:29 +10:00
Oliver
b1165af3c3 Merge pull request #1833 from SchrodingersGat/part-cat-form-improvements
Part cat form improvements
2021-07-18 21:32:31 +10:00
Oliver Walters
b04d6051a4 Disable secondary modals 2021-07-18 21:32:25 +10:00
Oliver Walters
e17b92b126 Add 'destination' field to POLineItem API serializer 2021-07-18 21:25:59 +10:00
Oliver Walters
c33cfe9503 Small rendering improvements 2021-07-18 21:17:03 +10:00
Oliver Walters
753fe9c80f Ordering and rendering for StockLocation 2021-07-18 21:15:51 +10:00
Oliver Walters
36cf614aed Add "level" to PartCategory serializer
- Also use tree ordering by default
2021-07-18 21:11:53 +10:00
Oliver
8c1248d74b Merge pull request #1828 from SchrodingersGat/supplier-reference-fiix
Add supplier_reference field to form
2021-07-18 11:04:14 +10:00
Matthias
eba5512a38 extending form for stock creation 2021-07-18 02:58:39 +02:00
Oliver Walters
f4f7514b45 Similar fix for customer_reference field 2021-07-18 10:47:35 +10:00
Oliver Walters
9719a14587 Add supplier_reference field to form 2021-07-18 10:33:27 +10:00
Matthias
44482800e4 switching out icon for setting 2021-07-18 02:27:15 +02:00
Matthias
210a4bccde setting for #1796 2021-07-18 02:20:56 +02:00
eeintech
f938e722b9 Another shot at it! 2021-07-16 12:08:26 -04:00
eeintech
86f3f56a60 Merge branch 'master' of github.com:inventree/InvenTree into part_main_details 2021-07-16 10:43:53 -04:00
Oliver
5ce6dd325d Merge pull request #1824 from SchrodingersGat/po-currency
Use default curreny code
2021-07-16 13:54:41 +10:00
Oliver
9ded804707 Merge pull request #1823 from SchrodingersGat/table-fixes
Link fixes
2021-07-16 13:43:04 +10:00
Oliver
733951883e Use default curreny code 2021-07-16 13:35:51 +10:00
Oliver
0fcb4e3170 Link fixes 2021-07-16 13:30:11 +10:00
Oliver
3f3d058bfa Merge pull request #1817 from SchrodingersGat/spa-bug-fix
Fixes for issues with new SPA approach
2021-07-16 07:33:28 +10:00
Oliver
db16b23287 Merge pull request #1820 from eeintech/fix_logout_css
Fix logout CSS
2021-07-16 07:33:11 +10:00
eeintech
886d95e467 Easy fix! 2021-07-15 16:31:07 -04:00
eeintech
bfc421c50b Cleaner part details 2021-07-15 14:52:33 -04:00
Oliver
cbbd58c743 Fixes for issues with new SPA approach
- Fix manufacturer part table
- Fix supplier part table
- Consolidate manufacturer and supplier parts onto single page
- CSS fixes
2021-07-16 01:13:48 +10:00
Oliver
bb60eed897 Merge pull request #1811 from SchrodingersGat/spa
Dynamically switch between navbar selections on pages
2021-07-15 23:43:43 +10:00
Oliver
28b3432afe Delete outdated unit test 2021-07-15 23:19:47 +10:00
Oliver
e86e15df99 Fix part import form 2021-07-15 23:18:41 +10:00
Oliver
e116ef9a8b Fix BOM upload form 2021-07-15 23:14:40 +10:00
Oliver
09d175f7cf Fix purchase order import form 2021-07-15 23:11:59 +10:00
Oliver
c61fc7c1df Refactor part pricing page 2021-07-15 23:06:37 +10:00
Oliver
0fc558068f Refactor BuildOrder pages 2021-07-15 22:40:14 +10:00
Oliver
533a3aa368 Refactor StockItem pages 2021-07-15 22:19:13 +10:00
Oliver
b1640fcc23 Refactor StockLocation pages 2021-07-15 21:38:05 +10:00
Oliver
676cca89a1 Refactor ManufacturerPart pages 2021-07-15 21:32:46 +10:00
Oliver
52da678636 Refactor SupplierPart page 2021-07-15 21:24:33 +10:00
Oliver
72337dab49 Refactor PartCategory views 2021-07-15 17:26:06 +10:00
Oliver
d5bf108fef Remove unused template file 2021-07-15 16:54:49 +10:00
Oliver
df89008116 Refactor allocation page(s)
- Also perform null check on notes before displaying
2021-07-15 16:53:14 +10:00
Oliver
e38d740bbc Refactor "used in" page 2021-07-15 16:44:46 +10:00
Oliver
e7c7bdcd00 Refactor BOM page 2021-07-15 16:42:28 +10:00
Oliver
57851b0eaf Refactor build orders display 2021-07-15 16:36:20 +10:00
Oliver
c8c7f78ce9 Refactor "related parts" view 2021-07-15 16:28:26 +10:00
Oliver
8607d702c4 Refactor variants page 2021-07-15 16:17:41 +10:00
Oliver
985bd39234 Fix links 2021-07-15 16:14:48 +10:00
Oliver
84149d34a6 Refactor part-supplier view 2021-07-15 16:12:27 +10:00
Oliver
984e16d5af Return all part parameters to the main part edit form
- refactor purchaseorder view
- refactor salesorder view
2021-07-15 16:07:46 +10:00
Oliver
352a58b373 Can select nav based on URL query parameter 2021-07-15 13:41:26 +10:00
Oliver
3786454e4c Remove URL 2021-07-15 12:56:26 +10:00
Oliver
1a30a5bc16 Merge "test template" page 2021-07-15 12:56:17 +10:00
Oliver
2d2ad91545 Move "stock" part view 2021-07-15 12:52:36 +10:00
Oliver
0667857754 Refactor part base display 2021-07-15 12:04:48 +10:00
Oliver
94792596e9 Update version.py
Bumped version number
2021-07-15 09:54:55 +10:00
Oliver
c816afbe62 Merge pull request #1812 from eeintech/improve_supplier_part_fetch
Custom SupplierPart object manager and prefetch related models
2021-07-14 07:59:25 +10:00
eeintech
3d9ad24e27 Defined custom SupplierPart object manager and prefetch related models in all queries 2021-07-13 12:55:36 -04:00
Oliver
8fed3b3522 Typo fix 2021-07-13 22:03:49 +10:00
Oliver
8dde89e781 Table fixes 2021-07-13 21:57:49 +10:00
Oliver
c1c0a262b2 Refactor Company detail view 2021-07-13 21:53:48 +10:00
Oliver
b6227f7d28 Refactor PurchaseOrder display 2021-07-13 21:21:00 +10:00
Oliver
65de52b705 Add javascript to dynamically switch between views 2021-07-13 20:58:05 +10:00
Oliver
9889e314a9 Remove unused templates 2021-07-13 20:04:12 +10:00
Oliver
970f08260c Update navbar 2021-07-13 20:04:02 +10:00
Oliver
39c58e4015 Move all "SalesOrder" content onto a single page 2021-07-13 19:59:11 +10:00
Oliver
a0b3359d62 Fix filtering for build orders 2021-07-13 19:58:47 +10:00
Oliver
2295008944 Merge pull request #1808 from SchrodingersGat/part-page-refactor
Move "attachments" and "notes" to "Part Detail" page
2021-07-12 23:56:40 +10:00
Oliver
cf23fb6fe8 PEP fixes 2021-07-12 23:32:08 +10:00
Oliver
b1af07c8cb Remove stale pages 2021-07-12 23:31:10 +10:00
Oliver
77d80f5c0f Move "attachments" and "notes" to "Part Detail" page
(cherry picked from commit daf0a082dc04c04cfd68cab70148a7d7cf28460f)
2021-07-12 23:27:50 +10:00
Oliver
6af05b2b83 Merge pull request #1807 from SchrodingersGat/forms-cleanup
Remove unused forms
2021-07-12 22:40:20 +10:00
Oliver
374c3676a3 Merge pull request #1806 from SchrodingersGat/manufacturer-part-api-form
Refactor ManufacturerPartCreate form
2021-07-12 22:23:15 +10:00
Oliver
0cb2b49333 Remove unused forms 2021-07-12 22:23:08 +10:00
Oliver
bd8b52d7d2 Merge pull request #1804 from SchrodingersGat/api-stock-adjustments
Api stock adjustments
2021-07-12 22:16:56 +10:00
Oliver
7c80767414 PEP fixes 2021-07-12 22:06:03 +10:00
Oliver
30fd3c8841 Unit test fixes 2021-07-12 22:03:36 +10:00
Oliver
e9c7937ef4 Merge pull request #1805 from SchrodingersGat/selec2-fix
Fix dodgy CSS file
2021-07-12 21:34:14 +10:00
Oliver
c3b0593aba Refactor ManufacturerPartCreate form 2021-07-12 21:32:09 +10:00
Oliver
ccf17bf4c5 Fix dodgy CSS file 2021-07-12 21:19:00 +10:00
Oliver
77cfadad42 Add 'title' option for contsructed fields 2021-07-12 21:11:29 +10:00
Oliver
52eedef820 remove old StockAdjust view 2021-07-12 21:03:28 +10:00
Oliver
edf4aab063 Refactor "showQuestionDialog" function 2021-07-12 21:03:01 +10:00
Oliver
a1579eecfd Refactor "showAlertDialog" function 2021-07-12 20:55:28 +10:00
Oliver
e9bf4b4cef Add some more functionality to StockLocation page 2021-07-12 20:53:36 +10:00
Oliver
2ea4824030 Add option to move stock for a part 2021-07-12 20:45:45 +10:00
Oliver
9fc7976569 Refactor all "adjustment" forms to use the new API approach 2021-07-12 20:38:54 +10:00
Oliver
74e5b2cd3f Handle delete differently 2021-07-12 20:06:53 +10:00
Oliver
2bebf2d41a Test fixes 2021-07-12 20:00:50 +10:00
Oliver
11ee8e8369 Raise error if specified quantity is above available quantity 2021-07-12 19:59:10 +10:00
Oliver
5329e3e56c Display per-line errors 2021-07-12 19:42:06 +10:00
Oliver
51314a0261 Refactor error messaging for stock adjustment API 2021-07-12 19:41:50 +10:00
Oliver
e04828214a Refactor showApiError() function 2021-07-12 19:20:29 +10:00
Oliver
0c41cc7c77 Handle form submissions 2021-07-12 18:13:06 +10:00
Oliver
e3f85414fa Stock API URL cleanup 2021-07-12 17:32:06 +10:00
Oliver
747cccfa42 Refactor to use more generic forms approach 2021-07-12 16:55:35 +10:00
Oliver
7531984c78 Fix read_only attribute 2021-07-11 21:17:54 +10:00
Oliver
cc90c8abbe Move buttons to separate table column 2021-07-11 21:15:06 +10:00
Oliver
9eb1367d80 Add "location" field 2021-07-11 21:07:56 +10:00
Oliver
3efd7f7777 Add a "notes" field 2021-07-11 20:56:29 +10:00
Oliver
ca5d3a57de Set quantity input parameters based on action 2021-07-11 20:49:54 +10:00
Oliver
9e4bc274cf Allow custom code to be run on form submission 2021-07-11 20:40:27 +10:00
Oliver
c045a3b6f6 Refactorin' 2021-07-11 20:36:52 +10:00
Oliver
0be787ea5b Merge pull request #1802 from matmair/next-btn-leadingzero
support leading zeros in serial numbers
2021-07-11 09:04:09 +10:00
Matthias
03fb6e5c61 support leading zeros in serial numbers 2021-07-10 17:54:17 +02:00
Oliver
14ab1bef14 Callback to remove row 2021-07-11 00:15:46 +10:00
Oliver
0507e8a3bc Building stock adjustment modal 2021-07-10 23:59:35 +10:00
Oliver
02214ea713 Merge pull request #1799 from SchrodingersGat/bom-export-fix
Bug fix for BOM export
2021-07-10 23:27:17 +10:00
Oliver
7cc1063114 PEP 2021-07-10 23:04:34 +10:00
Oliver
73e03636a2 Add unit tests 2021-07-10 23:04:20 +10:00
Oliver
d9c2d061cc Merge pull request #1797 from SchrodingersGat/bom-validation-fix
Add numerical validation step for BomItem
2021-07-10 14:14:52 +10:00
Oliver
cf66a386ea Bug fix 2021-07-10 14:13:46 +10:00
Oliver
bf2774eb21 Add numerical validation step for BomItem 2021-07-10 13:48:44 +10:00
Oliver
f6d5bd4ed8 Merge pull request #1760 from eeintech/bom_import
Converted BOM import to new multi-step form framework
2021-07-10 13:47:25 +10:00
Oliver
fe6da32e64 Merge pull request #1793 from SchrodingersGat/combine-manufacturer-supplier-tables
Consolidate Manufacturers and Suppliers views
2021-07-09 16:57:11 +10:00
Oliver
9ae5c241f5 Move part parameters onto the part details page 2021-07-09 16:34:47 +10:00
Oliver
ff92210b25 Fix URLs 2021-07-09 16:31:29 +10:00
Oliver
6eec6a0599 Consolidate Manufacturers and Suppliers views 2021-07-09 16:29:58 +10:00
Oliver
b89fe4f9d4 Merge pull request #1789 from SchrodingersGat/supplier-part-table
Add more fields to ManufacturerPart and SupplierPart tables
2021-07-09 15:09:39 +10:00
Oliver
47a56f7f5d Fixes for unit tests 2021-07-09 14:54:03 +10:00
Oliver
c694c9467b sessionStorage -> localStorage
OMG
2021-07-09 14:39:08 +10:00
Oliver
6b73e7a408 Add more fields to ManufacturerPart and SupplierPart tables 2021-07-09 14:28:32 +10:00
Oliver
5235ad1b0c Merge pull request #1787 from SchrodingersGat/bom-table-additions
Add sub_part_assembly filter to BOM table
2021-07-09 13:39:09 +10:00
Oliver
7168607a88 PEP fix 2021-07-09 13:07:01 +10:00
Oliver
079e331bf6 Add sub_part_assembly filter to BOM table 2021-07-09 13:04:48 +10:00
Oliver
558e24c985 Merge pull request #1783 from SchrodingersGat/build-table-filtering
Fix for strange table filtering bug
2021-07-09 12:57:03 +10:00
Oliver
c45e2d682e Merge pull request #1782 from SchrodingersGat/company-rendering
Improve rendering of "Company" in API form
2021-07-09 12:31:31 +10:00
Oliver
9d7f9a9aa3 Fix for strange table filtering bug
- When a table was "refreshed" based on the selectable filters, table ordering was not observed
- This was due to the original query parameter conversion not being observed
- Refactored out the conversion function so it works correctly now
- Also removed some cruft from generated query strings
2021-07-09 12:31:25 +10:00
Oliver
7539bd47fe Fix rendering issues 2021-07-09 12:10:27 +10:00
Oliver
6a995042c9 Fix bug relating no PurchaseOrderLineItem with null Part reference 2021-07-09 12:10:17 +10:00
Oliver
4b6ca548b6 Improve form rendering for SupplierPart 2021-07-09 12:09:57 +10:00
Oliver
40ac1f39e0 Improve rendering of "Company" in API form
- Add a thumbnail image
- Refactor select2 thumbnail code

(cherry picked from commit 2df7c520be)
2021-07-09 11:14:38 +10:00
Oliver
19ba9bf93c Merge pull request #1780 from SchrodingersGat/IPN_case
Fix case for filtering parts by IPN
2021-07-09 09:26:48 +10:00
Oliver
0c73fa3b58 Add regex filter for part name 2021-07-09 09:11:31 +10:00
Oliver
fd5d20ad13 Fix case for filtering parts by IPN 2021-07-09 09:08:55 +10:00
Oliver
433098ce6e Merge pull request #1778 from SchrodingersGat/build-forms
Refactor BuildOrderEdit form
2021-07-09 09:02:39 +10:00
Oliver
0a86d947bc Refactor PartParameterCreate form 2021-07-09 02:03:33 +10:00
Oliver
cbf0e0bd4a Refactor forms for editing and deleting a PartParameter
- PartParameters now loaded using the API, not pre-rendered
2021-07-09 01:56:04 +10:00
Oliver
cbd291849c More unit test fixes 2021-07-09 01:49:17 +10:00
Oliver
6e26bd0b71 Fixes for unit tests 2021-07-09 01:29:17 +10:00
Oliver
337223b8eb Modal form improvements 2021-07-09 01:20:40 +10:00
Oliver
ecf47aa69d Fix for BuildOrder reference default value 2021-07-09 01:20:29 +10:00
Oliver
60e8a17f07 bug fix: Prevent API forms from being submitted multiple times
- A problem if you're a manic clicker
2021-07-09 01:06:38 +10:00
Oliver
9947a0cf90 PEP fix 2021-07-09 00:59:56 +10:00
Oliver
7e7fe7d63f Refactor buildlist filtering 2021-07-09 00:58:17 +10:00
Oliver
bec98d355a Improvements
- part_detail defaults to True for BuildSerializer
- Handle invalid parent for BuildOrder
2021-07-09 00:49:19 +10:00
Oliver
be6ecd9587 Fixes for BuildOrder forms 2021-07-09 00:36:54 +10:00
Oliver
13ca076f42 Fix for form rendering of "required" fields with a default value
- Force the "required" parameter to be set
2021-07-09 00:29:36 +10:00
Oliver
004b36b1df Refactor BuildOrderCreate form 2021-07-09 00:18:03 +10:00
Oliver
5016d44b83 Add default value for BuildOrder reference 2021-07-09 00:15:49 +10:00
Oliver
245c04367b Refactor BuildOrderEdit form 2021-07-08 23:57:42 +10:00
Oliver
522432f4aa Merge pull request #1776 from SchrodingersGat/part-labels
Part labels
2021-07-08 23:34:59 +10:00
Oliver
bd4dde2cb7 Add permissions for PartLabel model 2021-07-08 23:22:25 +10:00
Oliver
15cb1e0005 Print part labels 2021-07-08 22:54:41 +10:00
Oliver
c39f705ef7 Copy default part label templates 2021-07-08 22:42:31 +10:00
Oliver
1830467487 Add admin / serializer / API for PartLabel model 2021-07-08 22:10:10 +10:00
Oliver
a1a4bddcc6 Add model for PartLabel 2021-07-08 21:07:45 +10:00
Oliver
0599fbaf26 Merge pull request #1773 from SchrodingersGat/ipn-filtering
API Filtering improvements
2021-07-08 20:47:07 +10:00
Oliver
a985e11aa8 Simplify and add filters for StockList API endpoint 2021-07-08 20:10:22 +10:00
Oliver
c7f79a5a08 Fixes 2021-07-08 19:23:01 +10:00
Oliver
79d90b1c4a Additional filtering options for name and IPN fields 2021-07-08 17:46:57 +10:00
Oliver
f0e7826fdc Adds some more API filters for the StockItem endpoint 2021-07-08 17:44:52 +10:00
Oliver
81010994e7 Adds regex filtering for "batch" code on StockItem 2021-07-08 17:26:55 +10:00
Oliver
a8a21f7c9d Transition "has IPN" filter to django-filters approach 2021-07-08 17:16:02 +10:00
Oliver
ba0a13443f PEP fixes 2021-07-08 17:02:55 +10:00
Oliver
376428b80b Add regex IPN filter for Part API 2021-07-08 17:02:45 +10:00
Oliver
800cb9606a Merge pull request #1772 from SchrodingersGat/part-stock-part-column
Reintroduces "part" column to part stock
2021-07-08 15:27:59 +10:00
Oliver
2467690111 Reintroduces "part" column to part stock 2021-07-08 13:46:53 +10:00
Oliver
a2870b60d9 Merge pull request #1757 from matmair/stock-next-prev
Stock previous / next serial
2021-07-08 11:41:01 +10:00
Oliver
f1797dbe2f Merge pull request #1771 from SchrodingersGat/company-part-search-fix
API: Allow search of IPN field for ManufacturerPart and SupplierPart
2021-07-08 11:30:58 +10:00
Oliver
ae68463f46 API: Allow search of IPN field for ManufacturerPart and SupplierPart 2021-07-08 11:16:04 +10:00
Oliver
309097293f Merge pull request #1768 from SchrodingersGat/attachment-fixes
Fixes for attachment issues
2021-07-08 11:12:34 +10:00
Oliver
fcc244b52f Fixes for BuildOrder attachment API list 2021-07-08 10:50:34 +10:00
Oliver
1deab0c040 Fix for SalesOrder attachment table 2021-07-08 10:48:08 +10:00
Oliver
939d5838fa Fixes for PurchaseOrder attachment table 2021-07-08 10:45:42 +10:00
Oliver
41268d92bf Add missing bootstrap .map file 2021-07-08 10:45:23 +10:00
Oliver
1a979fc113 Filtering fix for StockItem attachments 2021-07-08 10:41:18 +10:00
Oliver
ff8ddfbb5c Fix link to select2 2021-07-08 10:38:12 +10:00
Oliver
1dbdcb6d9c Merge pull request #1763 from eeintech/stock_table_supplier_part_detail
Fixed supplier part detail handling in stock table
2021-07-07 07:53:04 +10:00
Matthias
2db42eff50 remove unneeded tag 2021-07-06 21:25:42 +02:00
Matthias
98c52c06ac Revert "fetching supplier part for stock tables"
This reverts commit eaf191dc8d.
2021-07-06 21:20:27 +02:00
Matthias
9ed2338162 bolder type for serial numbers 2021-07-06 21:10:42 +02:00
eeintech
87a7081185 Fixed supplier part detail handling in stock table 2021-07-06 14:32:16 -04:00
Oliver
c2df1fcd95 Merge pull request #1749 from matmair/extend-build-order
Extend build order table
2021-07-06 11:23:36 +10:00
Oliver
db21ccdb30 Merge pull request #1761 from eeintech/stock_move_modal_notes
Fixes for stock modal and notes propagation to tracking items
2021-07-06 08:59:15 +10:00
eeintech
6687661928 Fixes for stock modal and notes propagation to tracking items 2021-07-05 17:36:10 -04:00
eeintech
58efc952db Converted BOM import to new multi-step form framework 2021-07-05 14:57:45 -04:00
Oliver
1d0dd04ca4 Merge pull request #1759 from SchrodingersGat/manufacturer-part-table-fix
Fix filters for ManufacturerPart and SupplierPart table views
2021-07-05 17:54:41 +10:00
Oliver
f72762ceb7 Fix filters for ManufacturerPart and SupplierPart table views 2021-07-05 17:15:24 +10:00
Oliver
f790d6a6a5 L10 (#1752)
* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* updated translation base

* updated translation base

* updated translation base

* updated translation base

* updated translation base

* New Crowdin updates (#1751)

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* updated translation base

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

* Fix: New translations django.po from Crowdin

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2021-07-05 16:54:15 +10:00
Matthias
84b67e2cc1 also allow the boundaries 2021-07-05 00:53:05 +02:00
Matthias
1786c029b4 prev and next serial link in stock items 2021-07-05 00:47:45 +02:00
Matthias
369acb494b new tag for url resolve 2021-07-05 00:46:51 +02:00
Matthias
eaf191dc8d fetching supplier part for stock tables 2021-07-04 23:45:04 +02:00
Matthias
4711f0b823 fromatter for responsible group 2021-07-04 00:16:14 +02:00
Matthias
7b3bc33b88 using owner serializer 2021-07-04 00:15:44 +02:00
Matthias
4f31cee6a3 Merge branch 'master' of https://github.com/inventree/InvenTree into extend-build-order 2021-07-04 00:02:29 +02:00
Matthias
ffa9dd18cf adding new fields into table 2021-07-03 01:49:18 +02:00
Matthias
4f432d4db2 extend api with issuer
#1356
2021-07-03 01:47:54 +02:00
rgilham
c6fd2281d6 Allow BOM pricing to be valid when using internal pricing 2021-06-24 02:13:55 +02:00
333 changed files with 21387 additions and 19866 deletions

View File

@@ -1,9 +0,0 @@
[run]
source = ./InvenTree
omit =
InvenTree/manage.py
InvenTree/setup.py
InvenTree/InvenTree/middleware.py
InvenTree/InvenTree/utils.py
InvenTree/InvenTree/wsgi.py
InvenTree/users/apps.py

25
.eslintrc.yml Normal file
View File

@@ -0,0 +1,25 @@
env:
commonjs: false
browser: true
es2021: true
jquery: true
extends:
- google
parserOptions:
ecmaVersion: 12
rules:
no-var: off
guard-for-in: off
no-trailing-spaces: off
camelcase: off
padded-blocks: off
prefer-const: off
max-len: off
require-jsdoc: off
valid-jsdoc: off
no-multiple-empty-lines: off
comma-dangle: off
prefer-spread: off
indent:
- error
- 4

2
.gitattributes vendored
View File

@@ -7,5 +7,5 @@
*.yml text
*.yaml text
*.conf text
*.sh text
*.sh text eol=lf
*.js text

30
.github/ISSUE_TEMPLATE/app_issue.md vendored Normal file
View File

@@ -0,0 +1,30 @@
---
name: App issue
about: Report a bug or issue with the InvenTree app
title: "[APP] Enter bug description"
labels: bug, app
assignees: ''
---
**Describe the bug**
A clear and concise description of the bug or issue
**To Reproduce**
Steps to reproduce the behavior:
1. Go to ...
2. Select ...
3. ...
**Expected Behavior**
A clear and concise description of what you expected to happen
**Screenshots**
If applicable, add screenshots to help explain your problem
**Version Information**
- App platform: *Select iOS or Android*
- App version: *Enter app version*
- Server version: *Enter server version*

31
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@@ -0,0 +1,31 @@
---
name: Bug report
about: Create a bug report to help us improve InvenTree
title: "[BUG] Enter bug description"
labels: bug, question
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Deployment Method**
Docker
Bare Metal
**Version Information**
You can get this by going to the "About InvenTree" section in the upper right corner and cicking on to the "copy version information"

View File

@@ -0,0 +1,26 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FR]"
labels: enhancement
assignees: ''
---
**Is your feature request the result of a bug?**
Please link it here.
**Problem**
A clear and concise description of what the problem is. e.g. I'm always frustrated when [...]
**Suggested solution**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Examples of other systems**
Show how other software handles your FR if you have examples.
**Do you want to develop this?**
If so please describe briefly how you would like to implement it (so we can give advice) and if you have experience in the needed technology (you do not need to be a pro - this is just as a information for us).

View File

@@ -15,6 +15,9 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Check version number
run: |
python3 ci/check_version_number.py --dev
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx

42
.github/workflows/docker_stable.yaml vendored Normal file
View File

@@ -0,0 +1,42 @@
# Build and push latest docker image on push to master branch
name: Docker Build
on:
push:
branches:
- 'stable'
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Check version number
run: |
python3 ci/check_version_number.py --release
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to Dockerhub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push
uses: docker/build-push-action@v2
with:
context: ./docker
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
target: production
build-args: |
branch=stable
repository: inventree/inventree
tags: inventree/inventree:stable
- name: Image Digest
run: echo ${{ steps.docker_build.outputs.digest }}

View File

@@ -13,6 +13,9 @@ jobs:
steps:
- name: Check out repo
uses: actions/checkout@v2
- name: Check Release tag
run: |
python3 ci/check_version_number.py --release --tag ${{ github.event.release.tag_name }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
@@ -29,5 +32,7 @@ jobs:
platforms: linux/amd64,linux/arm64,linux/arm/v7
push: true
target: production
build-args: |
tag=${{ github.event.release.tag_name }}
repository: inventree/inventree
tags: inventree/inventree:${{ github.event.release.tag_name }}

54
.github/workflows/html.yaml vendored Normal file
View File

@@ -0,0 +1,54 @@
# Check javascript template files
name: HTML Templates
on:
push:
branches:
- master
pull_request:
branches-ignore:
- l10*
jobs:
html:
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
INVENTREE_DB_ENGINE: sqlite3
INVENTREE_DB_NAME: inventree
INVENTREE_MEDIA_ROOT: ./media
INVENTREE_STATIC_ROOT: ./static
steps:
- name: Install node.js
uses: actions/setup-node@v2
- run: npm install
- name: Checkout Code
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.7
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install gettext
pip3 install invoke
invoke install
invoke static
- name: Check HTML Files
run: |
npm install markuplint
npx markuplint InvenTree/build/templates/build/*.html
npx markuplint InvenTree/common/templates/common/*.html
npx markuplint InvenTree/company/templates/company/*.html
npx markuplint InvenTree/order/templates/order/*.html
npx markuplint InvenTree/part/templates/part/*.html
npx markuplint InvenTree/stock/templates/stock/*.html
npx markuplint InvenTree/templates/*.html
npx markuplint InvenTree/templates/InvenTree/*.html
npx markuplint InvenTree/templates/InvenTree/settings/*.html

50
.github/workflows/javascript.yaml vendored Normal file
View File

@@ -0,0 +1,50 @@
# Check javascript template files
name: Javascript Templates
on:
push:
branches:
- master
pull_request:
branches-ignore:
- l10*
jobs:
javascript:
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
INVENTREE_DB_ENGINE: sqlite3
INVENTREE_DB_NAME: inventree
INVENTREE_MEDIA_ROOT: ./media
INVENTREE_STATIC_ROOT: ./static
steps:
- name: Install node.js
uses: actions/setup-node@v2
- run: npm install
- name: Checkout Code
uses: actions/checkout@v2
- name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.7
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install gettext
pip3 install invoke
invoke install
invoke static
- name: Check Templated Files
run: |
cd ci
python check_js_templates.py
- name: Lint Javascript Files
run: |
npm install eslint eslint-config-google
invoke render-js-files
npx eslint js_tmp/*.js

20
.github/workflows/version.yaml vendored Normal file
View File

@@ -0,0 +1,20 @@
# Check that the version number format matches the current branch
name: Version Numbering
on:
pull_request:
branches-ignore:
- l10*
jobs:
check:
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Check version number
run: |
python3 ci/check_version_number.py --branch ${{ github.base_ref }}

View File

@@ -1,29 +1,102 @@
Contributions to InvenTree are welcomed - please follow the guidelines below.
Please read the contribution guidelines below, before submitting your first pull request to the InvenTree codebase.
## Feature Branches
## Branches and Versioning
No pushing to master! New featues must be submitted in a separate branch (one branch per feature).
InvenTree roughly follow the [GitLab flow](https://docs.gitlab.com/ee/topics/gitlab_flow.html) branching style, to allow simple management of multiple tagged releases, short-lived branches, and development on the main branch.
## Include Migration Files
### Version Numbering
InvenTree version numbering follows the [semantic versioning](https://semver.org/) specification.
### Master Branch
The HEAD of the "main" or "master" branch of InvenTree represents the current "latest" state of code development.
- All feature branches are merged into master
- All bug fixes are merged into master
**No pushing to master:** New featues must be submitted as a pull request from a separate branch (one branch per feature).
#### Feature Branches
Feature branches should be branched *from* the *master* branch.
- One major feature per branch / pull request
- Feature pull requests are merged back *into* the master branch
- Features *may* also be merged into a release candidate branch
### Stable Branch
The HEAD of the "stable" branch represents the latest stable release code.
- Versioned releases are merged into the "stable" branch
- Bug fix branches are made *from* the "stable" branch
#### Release Candidate Branches
- Release candidate branches are made from master, and merged into stable.
- RC branches are targetted at a major/minor version e.g. "0.5"
- When a release candidate branch is merged into *stable*, the release is tagged
#### Bugfix Branches
- If a bug is discovered in a tagged release version of InvenTree, a "bugfix" or "hotfix" branch should be made *from* that tagged release
- When approved, the branch is merged back *into* stable, with an incremented PATCH number (e.g. 0.4.1 -> 0.4.2)
- The bugfix *must* also be cherry picked into the *master* branch.
## Migration Files
Any required migration files **must** be included in the commit, or the pull-request will be rejected. If you change the underlying database schema, make sure you run `invoke migrate` and commit the migration files before submitting the PR.
## Update Translation Files
*Note: A github action checks for unstaged migration files and will reject the PR if it finds any!*
Any PRs which update translatable strings (i.e. text strings that will appear in the web-front UI) must also update the translation (locale) files to include hooks for the translated strings.
## Unit Testing
*This does not mean that all translations must be provided, but that the translation files must include locations for the translated strings to be written.*
Any new code should be covered by unit tests - a submitted PR may not be accepted if the code coverage for any new features is insufficient, or the overall code coverage is decreased.
To perform this step, simply run `invoke translate` from the top level directory before submitting the PR.
The InvenTree code base makes use of [GitHub actions](https://github.com/features/actions) to run a suite of automated tests against the code base every time a new pull request is received. These actions include (but are not limited to):
## Testing
- Checking Python and Javascript code against standard style guides
- Running unit test suite
- Automated building and pushing of docker images
- Generating translation files
Any new code should be covered by unit tests - a submitted PR may not be accepted if the code coverage is decreased.
The various github actions can be found in the `./github/workflows` directory
## Code Style
Sumbitted Python code is automatically checked against PEP style guidelines. Locally you can run `invoke style` to ensure the style checks will pass, before submitting the PR.
## Documentation
New features or updates to existing features should be accompanied by user documentation. A PR with associated documentation should link to the matching PR at https://github.com/inventree/inventree-docs/
## Code Style
## Translations
Sumbitted Python code is automatically checked against PEP style guidelines. Locally you can run `invoke style` to ensure the style checks will pass, before submitting the PR.
Any user-facing strings *must* be passed through the translation engine.
- InvenTree code is written in English
- User translatable strings are provided in English as the primary language
- Secondary language translations are provided [via Crowdin](https://crowdin.com/project/inventree)
*Note: Translation files are updated via GitHub actions - you do not need to compile translations files before submitting a pull request!*
### Python Code
For strings exposed via Python code, use the following format:
```python
from django.utils.translation import ugettext_lazy as _
user_facing_string = _('This string will be exposed to the translation engine!')
```
### Templated Strings
HTML and javascript files are passed through the django templating engine. Translatable strings are implemented as follows:
```html
{% load i18n %}
<span>{% trans "This string will be translated" %} - this string will not!</span>
```

View File

@@ -32,27 +32,44 @@ class InvenTreeConfig(AppConfig):
logger.info("Starting background tasks...")
# Remove successful task results from the database
InvenTree.tasks.schedule_task(
'InvenTree.tasks.delete_successful_tasks',
schedule_type=Schedule.DAILY,
)
# Check for InvenTree updates
InvenTree.tasks.schedule_task(
'InvenTree.tasks.check_for_updates',
schedule_type=Schedule.DAILY
)
# Heartbeat to let the server know the background worker is running
InvenTree.tasks.schedule_task(
'InvenTree.tasks.heartbeat',
schedule_type=Schedule.MINUTES,
minutes=15
)
# Keep exchange rates up to date
InvenTree.tasks.schedule_task(
'InvenTree.tasks.update_exchange_rates',
schedule_type=Schedule.DAILY,
)
# Remove expired sessions
InvenTree.tasks.schedule_task(
'InvenTree.tasks.delete_expired_sessions',
schedule_type=Schedule.DAILY,
)
# Delete "old" stock items
InvenTree.tasks.schedule_task(
'stock.tasks.delete_old_stock_items',
schedule_type=Schedule.MINUTES,
minutes=30,
)
def update_exchange_rates(self):
"""
Update exchange rates each time the server is started, *if*:

View File

@@ -0,0 +1,100 @@
"""
Pull rendered copies of the templated
"""
from django.http import response
from django.test import TestCase, testcases
from django.contrib.auth import get_user_model
import os
import pathlib
class RenderJavascriptFiles(TestCase):
"""
A unit test to "render" javascript files.
The server renders templated javascript files,
we need the fully-rendered files for linting and static tests.
"""
def setUp(self):
user = get_user_model()
self.user = user.objects.create_user(
username='testuser',
password='testpassword',
email='user@gmail.com',
)
self.client.login(username='testuser', password='testpassword')
def download_file(self, filename, prefix):
url = os.path.join(prefix, filename)
response = self.client.get(url)
here = os.path.abspath(os.path.dirname(__file__))
output_dir = os.path.join(
here,
'..',
'..',
'js_tmp',
)
output_dir = os.path.abspath(output_dir)
if not os.path.exists(output_dir):
os.mkdir(output_dir)
output_file = os.path.join(
output_dir,
filename,
)
with open(output_file, 'wb') as output:
output.write(response.content)
def download_files(self, subdir, prefix):
here = os.path.abspath(os.path.dirname(__file__))
js_template_dir = os.path.join(
here,
'..',
'templates',
'js',
)
directory = os.path.join(js_template_dir, subdir)
directory = os.path.abspath(directory)
js_files = pathlib.Path(directory).rglob('*.js')
n = 0
for f in js_files:
js = os.path.basename(f)
self.download_file(js, prefix)
n += 1
return n
def test_render_files(self):
"""
Look for all javascript files
"""
n = 0
print("Rendering javascript files...")
n += self.download_files('translated', '/js/i18n')
n += self.download_files('dynamic', '/js/dynamic')
print(f"Rendered {n} javascript files.")

View File

@@ -36,9 +36,14 @@ def health_status(request):
'email_configured': InvenTree.status.is_email_configured(),
}
# The following keys are required to denote system health
health_keys = [
'django_q_running',
]
all_healthy = True
for k in status.keys():
for k in health_keys:
if status[k] is not True:
all_healthy = False

View File

@@ -1,4 +1,5 @@
from common.settings import currency_code_default, currency_codes
from urllib.error import HTTPError, URLError
from djmoney.contrib.exchange.backends.base import SimpleExchangeBackend
@@ -26,4 +27,8 @@ class InvenTreeExchange(SimpleExchangeBackend):
symbols = ','.join(currency_codes())
super().update_rates(base=base_currency, symbols=symbols)
try:
super().update_rates(base=base_currency, symbols=symbols)
# catch connection errors
except (HTTPError, URLError):
print('Encountered connection error while updating')

View File

@@ -20,7 +20,6 @@ from djmoney.forms.fields import MoneyField
from djmoney.models.validators import MinMoneyValidator
import InvenTree.helpers
import common.settings
class InvenTreeURLFormField(FormURLField):
@@ -42,9 +41,11 @@ class InvenTreeURLField(models.URLField):
def money_kwargs():
""" returns the database settings for MoneyFields """
from common.settings import currency_code_mappings, currency_code_default
kwargs = {}
kwargs['currency_choices'] = common.settings.currency_code_mappings()
kwargs['default_currency'] = common.settings.currency_code_default
kwargs['currency_choices'] = currency_code_mappings()
kwargs['default_currency'] = currency_code_default()
return kwargs
@@ -55,7 +56,7 @@ class InvenTreeModelMoneyField(ModelMoneyField):
def __init__(self, **kwargs):
# detect if creating migration
if 'makemigrations' in sys.argv:
if 'migrate' in sys.argv or 'makemigrations' in sys.argv:
# remove currency information for a clean migration
kwargs['default_currency'] = ''
kwargs['currency_choices'] = []

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from rest_framework.filters import OrderingFilter
class InvenTreeOrderingFilter(OrderingFilter):
"""
Custom OrderingFilter class which allows aliased filtering of related fields.
To use, simply specify this filter in the "filter_backends" section.
filter_backends = [
InvenTreeOrderingFilter,
]
Then, specify a ordering_field_aliases attribute:
ordering_field_alises = {
'name': 'part__part__name',
'SKU': 'part__SKU',
}
"""
def get_ordering(self, request, queryset, view):
ordering = super().get_ordering(request, queryset, view)
aliases = getattr(view, 'ordering_field_aliases', None)
# Attempt to map ordering fields based on provided aliases
if ordering is not None and aliases is not None:
"""
Ordering fields should be mapped to separate fields
"""
for idx, field in enumerate(ordering):
reverse = False
if field.startswith('-'):
field = field[1:]
reverse = True
if field in aliases:
ordering[idx] = aliases[field]
if reverse:
ordering[idx] = '-' + ordering[idx]
return ordering

View File

@@ -13,7 +13,6 @@ from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field
from crispy_forms.bootstrap import PrependedText, AppendedText, PrependedAppendedText, StrictButton, Div
from common.models import ColorTheme
from part.models import PartCategory
@@ -177,39 +176,6 @@ class SetPasswordForm(HelperForm):
]
class ColorThemeSelectForm(forms.ModelForm):
""" Form for setting color theme """
name = forms.ChoiceField(choices=(), required=False)
class Meta:
model = ColorTheme
fields = [
'name'
]
def __init__(self, *args, **kwargs):
super(ColorThemeSelectForm, self).__init__(*args, **kwargs)
# Populate color themes choices
self.fields['name'].choices = ColorTheme.get_color_themes_choices()
self.helper = FormHelper()
# Form rendering
self.helper.form_show_labels = False
self.helper.layout = Layout(
Div(
Div(Field('name'),
css_class='col-sm-6',
style='width: 200px;'),
Div(StrictButton(_('Apply Theme'), css_class='btn btn-primary', type='submit'),
css_class='col-sm-6',
style='width: auto;'),
css_class='row',
),
)
class SettingCategorySelectForm(forms.ModelForm):
""" Form for setting category settings """

View File

@@ -344,13 +344,15 @@ def GetExportFormats():
]
def DownloadFile(data, filename, content_type='application/text'):
""" Create a dynamic file for the user to download.
def DownloadFile(data, filename, content_type='application/text', inline=False):
"""
Create a dynamic file for the user to download.
Args:
data: Raw file data (string or bytes)
filename: Filename for the file download
content_type: Content type for the download
inline: Download "inline" or as attachment? (Default = attachment)
Return:
A StreamingHttpResponse object wrapping the supplied data
@@ -365,7 +367,10 @@ def DownloadFile(data, filename, content_type='application/text'):
response = StreamingHttpResponse(wrapper, content_type=content_type)
response['Content-Length'] = len(data)
response['Content-Disposition'] = 'attachment; filename={f}'.format(f=filename)
disposition = "inline" if inline else "attachment"
response['Content-Disposition'] = f'{disposition}; filename={filename}'
return response
@@ -631,13 +636,34 @@ def clean_decimal(number):
""" Clean-up decimal value """
# Check if empty
if number is None or number == '':
if number is None or number == '' or number == 0:
return Decimal(0)
# Check if decimal type
# Convert to string and remove spaces
number = str(number).replace(' ', '')
# Guess what type of decimal and thousands separators are used
count_comma = number.count(',')
count_point = number.count('.')
if count_comma == 1:
# Comma is used as decimal separator
if count_point > 0:
# Points are used as thousands separators: remove them
number = number.replace('.', '')
# Replace decimal separator with point
number = number.replace(',', '.')
elif count_point == 1:
# Point is used as decimal separator
if count_comma > 0:
# Commas are used as thousands separators: remove them
number = number.replace(',', '')
# Convert to Decimal type
try:
clean_number = Decimal(number)
except InvalidOperation:
clean_number = number
# Number cannot be converted to Decimal (eg. a string containing letters)
return Decimal(0)
return clean_number.quantize(Decimal(1)) if clean_number == clean_number.to_integral() else clean_number.normalize()

View File

@@ -0,0 +1,38 @@
"""
Custom management command to cleanup old settings that are not defined anymore
"""
from django.core.management.base import BaseCommand
class Command(BaseCommand):
"""
Cleanup old (undefined) settings in the database
"""
def handle(self, *args, **kwargs):
print("Collecting settings")
from common.models import InvenTreeSetting, InvenTreeUserSetting
# general settings
db_settings = InvenTreeSetting.objects.all()
model_settings = InvenTreeSetting.GLOBAL_SETTINGS
# check if key exist and delete if not
for setting in db_settings:
if setting.key not in model_settings:
setting.delete()
print(f"deleted setting '{setting.key}'")
# user settings
db_settings = InvenTreeUserSetting.objects.all()
model_settings = InvenTreeUserSetting.GLOBAL_SETTINGS
# check if key exist and delete if not
for setting in db_settings:
if setting.key not in model_settings:
setting.delete()
print(f"deleted user setting '{setting.key}'")
print("checked all settings")

View File

@@ -32,6 +32,9 @@ class InvenTreeMetadata(SimpleMetadata):
def determine_metadata(self, request, view):
self.request = request
self.view = view
metadata = super().determine_metadata(request, view)
user = request.user
@@ -95,10 +98,12 @@ class InvenTreeMetadata(SimpleMetadata):
serializer_info = super().get_serializer_info(serializer)
try:
ModelClass = serializer.Meta.model
model_class = None
model_fields = model_meta.get_field_info(ModelClass)
try:
model_class = serializer.Meta.model
model_fields = model_meta.get_field_info(model_class)
# Iterate through simple fields
for name, field in model_fields.fields.items():
@@ -136,6 +141,54 @@ class InvenTreeMetadata(SimpleMetadata):
except AttributeError:
pass
# Try to extract 'instance' information
instance = None
# Extract extra information if an instance is available
if hasattr(serializer, 'instance'):
instance = serializer.instance
if instance is None and model_class is not None:
# Attempt to find the instance based on kwargs lookup
kwargs = getattr(self.view, 'kwargs', None)
if kwargs:
pk = None
for field in ['pk', 'id', 'PK', 'ID']:
if field in kwargs:
pk = kwargs[field]
break
if pk is not None:
try:
instance = model_class.objects.get(pk=pk)
except (ValueError, model_class.DoesNotExist):
pass
if instance is not None:
"""
If there is an instance associated with this API View,
introspect that instance to find any specific API info.
"""
if hasattr(instance, 'api_instance_filters'):
instance_filters = instance.api_instance_filters()
for field_name, field_filters in instance_filters.items():
if field_name not in serializer_info.keys():
# The field might be missing, but is added later on
# This function seems to get called multiple times?
continue
if 'instance_filters' not in serializer_info[field_name].keys():
serializer_info[field_name]['instance_filters'] = {}
for key, value in field_filters.items():
serializer_info[field_name]['instance_filters'][key] = value
return serializer_info
def get_field_info(self, field):
@@ -153,6 +206,11 @@ class InvenTreeMetadata(SimpleMetadata):
if 'default' not in field_info and not field.default == empty:
field_info['default'] = field.get_default()
# Force non-nullable fields to read as "required"
# (even if there is a default value!)
if not field.allow_null and not (hasattr(field, 'allow_blank') and field.allow_blank):
field_info['required'] = True
# Introspect writable related fields
if field_info['type'] == 'field' and not field_info['read_only']:
@@ -166,7 +224,12 @@ class InvenTreeMetadata(SimpleMetadata):
if model:
# Mark this field as "related", and point to the URL where we can get the data!
field_info['type'] = 'related field'
field_info['api_url'] = model.get_api_url()
field_info['model'] = model._meta.model_name
# Special case for 'user' model
if field_info['model'] == 'user':
field_info['api_url'] = '/api/user/'
else:
field_info['api_url'] = model.get_api_url()
return field_info

View File

@@ -21,28 +21,15 @@ class AuthRequiredMiddleware(object):
assert hasattr(request, 'user')
response = self.get_response(request)
# API requests are handled by the DRF library
if request.path_info.startswith('/api/'):
return self.get_response(request)
if not request.user.is_authenticated:
"""
Normally, a web-based session would use csrftoken based authentication.
However when running an external application (e.g. the InvenTree app),
we wish to use token-based auth to grab media files.
So, we will allow token-based authentication but ONLY for the /media/ directory.
What problem is this solving?
- The InvenTree mobile app does not use csrf token auth
- Token auth is used by the Django REST framework, but that is under the /api/ endpoint
- Media files (e.g. Part images) are required to be served to the app
- We do not want to make /media/ files accessible without login!
There is PROBABLY a better way of going about this?
a) Allow token-based authentication against a user?
b) Serve /media/ files in a duplicate location e.g. /api/media/ ?
c) Is there a "standard" way of solving this problem?
My [google|stackoverflow]-fu has failed me. So this hack has been created.
However when running an external application (e.g. the InvenTree app or Python library),
we must validate the user token manually.
"""
authorized = False
@@ -56,20 +43,23 @@ class AuthRequiredMiddleware(object):
elif request.path_info.startswith('/accounts/'):
authorized = True
elif 'Authorization' in request.headers.keys():
auth = request.headers['Authorization'].strip()
elif 'Authorization' in request.headers.keys() or 'authorization' in request.headers.keys():
auth = request.headers.get('Authorization', request.headers.get('authorization')).strip()
if auth.startswith('Token') and len(auth.split()) == 2:
token = auth.split()[1]
if auth.lower().startswith('token') and len(auth.split()) == 2:
token_key = auth.split()[1]
# Does the provided token match a valid user?
if Token.objects.filter(key=token).exists():
try:
token = Token.objects.get(key=token_key)
allowed = ['/api/', '/media/']
# Provide the user information to the request
request.user = token.user
authorized = True
# Only allow token-auth for /media/ or /static/ dirs!
if any([request.path_info.startswith(a) for a in allowed]):
authorized = True
except Token.DoesNotExist:
logger.warning(f"Access denied for unknown token {token_key}")
pass
# No authorization was found for the request
if not authorized:
@@ -92,8 +82,7 @@ class AuthRequiredMiddleware(object):
return redirect('%s?next=%s' % (reverse_lazy('login'), request.path))
# Code to be executed for each request/response after
# the view is called.
response = self.get_response(request)
return response

View File

@@ -5,8 +5,10 @@ Generic models which provide extra functionality over base Django model types.
from __future__ import unicode_literals
import os
import logging
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext_lazy as _
@@ -21,6 +23,9 @@ from mptt.exceptions import InvalidMove
from .validators import validate_tree_name
logger = logging.getLogger('inventree')
def rename_attachment(instance, filename):
"""
Function for renaming an attachment file.
@@ -77,6 +82,72 @@ class InvenTreeAttachment(models.Model):
def basename(self):
return os.path.basename(self.attachment.name)
@basename.setter
def basename(self, fn):
"""
Function to rename the attachment file.
- Filename cannot be empty
- Filename cannot contain illegal characters
- Filename must specify an extension
- Filename cannot match an existing file
"""
fn = fn.strip()
if len(fn) == 0:
raise ValidationError(_('Filename must not be empty'))
attachment_dir = os.path.join(
settings.MEDIA_ROOT,
self.getSubdir()
)
old_file = os.path.join(
settings.MEDIA_ROOT,
self.attachment.name
)
new_file = os.path.join(
settings.MEDIA_ROOT,
self.getSubdir(),
fn
)
new_file = os.path.abspath(new_file)
# Check that there are no directory tricks going on...
if not os.path.dirname(new_file) == attachment_dir:
logger.error(f"Attempted to rename attachment outside valid directory: '{new_file}'")
raise ValidationError(_("Invalid attachment directory"))
# Ignore further checks if the filename is not actually being renamed
if new_file == old_file:
return
forbidden = ["'", '"', "#", "@", "!", "&", "^", "<", ">", ":", ";", "/", "\\", "|", "?", "*", "%", "~", "`"]
for c in forbidden:
if c in fn:
raise ValidationError(_(f"Filename contains illegal character '{c}'"))
if len(fn.split('.')) < 2:
raise ValidationError(_("Filename missing extension"))
if not os.path.exists(old_file):
logger.error(f"Trying to rename attachment '{old_file}' which does not exist")
return
if os.path.exists(new_file):
raise ValidationError(_("Attachment with this filename already exists"))
try:
os.rename(old_file, new_file)
self.attachment.name = os.path.join(self.getSubdir(), fn)
self.save()
except:
raise ValidationError(_("Error renaming file"))
class Meta:
abstract = True
@@ -93,6 +164,17 @@ class InvenTreeTree(MPTTModel):
parent: The item immediately above this one. An item with a null parent is a top-level item
"""
def api_instance_filters(self):
"""
Instance filters for InvenTreeTree models
"""
return {
'parent': {
'exclude_tree': self.pk,
}
}
def save(self, *args, **kwargs):
try:

View File

@@ -10,6 +10,8 @@ import os
from decimal import Decimal
from collections import OrderedDict
from django.conf import settings
from django.contrib.auth.models import User
from django.core.exceptions import ValidationError as DjangoValidationError
@@ -46,10 +48,12 @@ class InvenTreeMoneySerializer(MoneyField):
amount = None
try:
if amount is not None:
if amount is not None and amount is not empty:
amount = Decimal(amount)
except:
raise ValidationError(_("Must be a valid number"))
raise ValidationError({
self.field_name: _("Must be a valid number")
})
currency = data.get(get_currency_field_name(self.field_name), self.default_currency)
@@ -85,14 +89,21 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
"""
def __init__(self, instance=None, data=empty, **kwargs):
# self.instance = instance
"""
Custom __init__ routine to ensure that *default* values (as specified in the ORM)
are used by the DRF serializers, *if* the values are not provided by the user.
"""
# If instance is None, we are creating a new instance
if instance is None and data is not empty:
# Required to side-step immutability of a QueryDict
data = data.copy()
if data is None:
data = OrderedDict()
else:
new_data = OrderedDict()
new_data.update(data)
data = new_data
# Add missing fields which have default values
ModelClass = self.Meta.model
@@ -165,6 +176,18 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
return self.instance
def update(self, instance, validated_data):
"""
Catch any django ValidationError, and re-throw as a DRF ValidationError
"""
try:
instance = super().update(instance, validated_data)
except (ValidationError, DjangoValidationError) as exc:
raise ValidationError(detail=serializers.as_serializer_error(exc))
return instance
def run_validation(self, data=empty):
"""
Perform serializer validation.
@@ -186,18 +209,45 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
# Update instance fields
for attr, value in data.items():
setattr(instance, attr, value)
try:
setattr(instance, attr, value)
except (ValidationError, DjangoValidationError) as exc:
raise ValidationError(detail=serializers.as_serializer_error(exc))
# Run a 'full_clean' on the model.
# Note that by default, DRF does *not* perform full model validation!
try:
instance.full_clean()
except (ValidationError, DjangoValidationError) as exc:
raise ValidationError(detail=serializers.as_serializer_error(exc))
data = exc.message_dict
# Change '__all__' key (django style) to 'non_field_errors' (DRF style)
if '__all__' in data:
data['non_field_errors'] = data['__all__']
del data['__all__']
raise ValidationError(data)
return data
class InvenTreeAttachmentSerializer(InvenTreeModelSerializer):
"""
Special case of an InvenTreeModelSerializer, which handles an "attachment" model.
The only real addition here is that we support "renaming" of the attachment file.
"""
# The 'filename' field must be present in the serializer
filename = serializers.CharField(
label=_('Filename'),
required=False,
source='basename',
allow_blank=False,
)
class InvenTreeAttachmentSerializerField(serializers.FileField):
"""
Override the DRF native FileField serializer,

View File

@@ -12,6 +12,7 @@ database setup in this file.
"""
import logging
import os
import random
import string
@@ -168,6 +169,30 @@ else:
logger.exception(f"Couldn't load keyfile {key_file}")
sys.exit(-1)
# The filesystem location for served static files
STATIC_ROOT = os.path.abspath(
get_setting(
'INVENTREE_STATIC_ROOT',
CONFIG.get('static_root', None)
)
)
if STATIC_ROOT is None:
print("ERROR: INVENTREE_STATIC_ROOT directory not defined")
sys.exit(1)
# The filesystem location for served static files
MEDIA_ROOT = os.path.abspath(
get_setting(
'INVENTREE_MEDIA_ROOT',
CONFIG.get('media_root', None)
)
)
if MEDIA_ROOT is None:
print("ERROR: INVENTREE_MEDIA_ROOT directory is not defined")
sys.exit(1)
# List of allowed hosts (default = allow all)
ALLOWED_HOSTS = CONFIG.get('allowed_hosts', ['*'])
@@ -188,22 +213,12 @@ if cors_opt:
# Web URL endpoint for served static files
STATIC_URL = '/static/'
# The filesystem location for served static files
STATIC_ROOT = os.path.abspath(
get_setting(
'INVENTREE_STATIC_ROOT',
CONFIG.get('static_root', '/home/inventree/data/static')
)
)
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'InvenTree', 'static'),
]
STATICFILES_DIRS = []
# Translated Template settings
STATICFILES_I18_PREFIX = 'i18n'
STATICFILES_I18_SRC = os.path.join(BASE_DIR, 'templates', 'js')
STATICFILES_I18_TRG = STATICFILES_DIRS[0] + '_' + STATICFILES_I18_PREFIX
STATICFILES_I18_SRC = os.path.join(BASE_DIR, 'templates', 'js', 'translated')
STATICFILES_I18_TRG = os.path.join(BASE_DIR, 'InvenTree', 'static_i18n')
STATICFILES_DIRS.append(STATICFILES_I18_TRG)
STATICFILES_I18_TRG = os.path.join(STATICFILES_I18_TRG, STATICFILES_I18_PREFIX)
@@ -217,19 +232,11 @@ STATIC_COLOR_THEMES_DIR = os.path.join(STATIC_ROOT, 'css', 'color-themes')
# Web URL endpoint for served media files
MEDIA_URL = '/media/'
# The filesystem location for served static files
MEDIA_ROOT = os.path.abspath(
get_setting(
'INVENTREE_MEDIA_ROOT',
CONFIG.get('media_root', '/home/inventree/data/media')
)
)
if DEBUG:
logger.info("InvenTree running in DEBUG mode")
logger.info(f"MEDIA_ROOT: '{MEDIA_ROOT}'")
logger.info(f"STATIC_ROOT: '{STATIC_ROOT}'")
logger.debug(f"MEDIA_ROOT: '{MEDIA_ROOT}'")
logger.debug(f"STATIC_ROOT: '{STATIC_ROOT}'")
# Application definition
@@ -319,6 +326,7 @@ TEMPLATES = [
'django.template.context_processors.i18n',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
# Custom InvenTree context processors
'InvenTree.context.health_status',
'InvenTree.context.status_codes',
'InvenTree.context.user_roles',
@@ -347,10 +355,22 @@ REST_FRAMEWORK = {
WSGI_APPLICATION = 'InvenTree.wsgi.application'
background_workers = os.environ.get('INVENTREE_BACKGROUND_WORKERS', None)
if background_workers is not None:
try:
background_workers = int(background_workers)
except ValueError:
background_workers = None
if background_workers is None:
# Sensible default?
background_workers = 4
# django-q configuration
Q_CLUSTER = {
'name': 'InvenTree',
'workers': 4,
'workers': background_workers,
'timeout': 90,
'retry': 120,
'queue_limit': 50,
@@ -400,7 +420,7 @@ Configure the database backend based on the user-specified values.
- The following code lets the user "mix and match" database configuration
"""
logger.info("Configuring database backend:")
logger.debug("Configuring database backend:")
# Extract database configuration from the config.yaml file
db_config = CONFIG.get('database', {})
@@ -454,11 +474,9 @@ if db_engine in ['sqlite3', 'postgresql', 'mysql']:
db_name = db_config['NAME']
db_host = db_config.get('HOST', "''")
print("InvenTree Database Configuration")
print("================================")
print(f"ENGINE: {db_engine}")
print(f"NAME: {db_name}")
print(f"HOST: {db_host}")
logger.info(f"DB_ENGINE: {db_engine}")
logger.info(f"DB_NAME: {db_name}")
logger.info(f"DB_HOST: {db_host}")
DATABASES['default'] = db_config
@@ -502,11 +520,24 @@ LANGUAGE_CODE = CONFIG.get('language', 'en-us')
# If a new language translation is supported, it must be added here
LANGUAGES = [
('en', _('English')),
('fr', _('French')),
('de', _('German')),
('el', _('Greek')),
('en', _('English')),
('es', _('Spanish')),
('fr', _('French')),
('he', _('Hebrew')),
('it', _('Italian')),
('ja', _('Japanese')),
('ko', _('Korean')),
('nl', _('Dutch')),
('no', _('Norwegian')),
('pl', _('Polish')),
('ru', _('Russian')),
('sv', _('Swedish')),
('th', _('Thai')),
('tr', _('Turkish')),
('vi', _('Vietnamese')),
('zh-cn', _('Chinese')),
]
# Currencies available for use

File diff suppressed because one or more lines are too long

View File

@@ -640,6 +640,11 @@
z-index: 9999;
}
.modal-error {
border: 2px #FCC solid;
background-color: #f5f0f0;
}
.modal-header {
border-bottom: 1px solid #ddd;
}
@@ -730,6 +735,13 @@
padding: 10px;
}
.form-panel {
border-radius: 5px;
border: 1px solid #ccc;
padding: 5px;
}
.modal input {
width: 100%;
}
@@ -781,8 +793,8 @@ input[type="submit"] {
}
.sidenav .list-group-item.active {
background-color: #ddd;
border-color: #ccc;
background-color: #c6d4ea;
border: 2px #aab solid;
}
/* The side navigation menu */
@@ -837,6 +849,12 @@ input[type="submit"] {
pointer-events: none; /* Prevent this div from blocking links underneath */
}
.notes {
border-radius: 5px;
background-color: #fafafa;
padding: 5px;
}
.alert {
display: none;
border-radius: 5px;
@@ -853,6 +871,11 @@ input[type="submit"] {
margin-right: 2px;
}
.btn-small {
padding: 3px;
padding-left: 5px;
}
.btn-remove {
padding: 3px;
padding-left: 5px;
@@ -901,6 +924,10 @@ input[type="submit"] {
box-shadow: 1px 1px #DDD;
}
.panel-hidden {
display: none;
}
.float-right {
float: right;
}
@@ -1022,3 +1049,19 @@ a.anchor {
height: 30px;
}
/* Force minimum width of number input fields to show at least ~5 digits */
input[type='number']{
min-width: 80px;
}
.search-menu {
padding-top: 2rem;
}
.search-menu .ui-menu-item {
margin-top: 0.5rem;
}
.product-card-panel{
height: 100%;
}

View File

@@ -1,334 +0,0 @@
function attachClipboard(selector, containerselector, textElement) {
// set container
if (containerselector){
containerselector = document.getElementById(containerselector);
} else {
containerselector = document.body;
}
// set text-function
if (textElement){
text = function() {
return document.getElementById(textElement).textContent;
}
} else {
text = function(trigger) {
var content = trigger.parentElement.parentElement.textContent;return content.trim();
}
}
// create Clipboard
var cis = new ClipboardJS(selector, {
text: text,
container: containerselector
});
}
function inventreeDocReady() {
/* Run this function when the HTML document is loaded.
* This will be called for every page that extends "base.html"
*/
window.addEventListener("dragover",function(e){
e = e || event;
e.preventDefault();
},false);
window.addEventListener("drop",function(e){
e = e || event;
e.preventDefault();
},false);
/* Add drag-n-drop functionality to any element
* marked with the class 'dropzone'
*/
$('.dropzone').on('dragenter', function(event) {
// TODO - Only indicate that a drop event will occur if a file is being dragged
var transfer = event.originalEvent.dataTransfer;
if (true || isFileTransfer(transfer)) {
$(this).addClass('dragover');
}
});
$('.dropzone').on('dragleave drop', function(event) {
$(this).removeClass('dragover');
});
// Callback to launch the 'About' window
$('#launch-about').click(function() {
var modal = $('#modal-about');
modal.modal({
backdrop: 'static',
keyboard: 'false',
});
modal.modal('show');
});
// Callback to launch the 'Database Stats' window
$('#launch-stats').click(function() {
launchModalForm("/stats/", {
no_post: true,
});
});
// Initialize clipboard-buttons
attachClipboard('.clip-btn');
attachClipboard('.clip-btn', 'modal-about'); // modals
attachClipboard('.clip-btn-version', 'modal-about', 'about-copy-text'); // version-text
}
function isFileTransfer(transfer) {
/* Determine if a transfer (e.g. drag-and-drop) is a file transfer
*/
return transfer.files.length > 0;
}
function isOnlineTransfer(transfer) {
/* Determine if a drag-and-drop transfer is from another website.
* e.g. dragged from another browser window
*/
return transfer.items.length > 0;
}
function getImageUrlFromTransfer(transfer) {
/* Extract external image URL from a drag-and-dropped image
*/
var url = transfer.getData('text/html').match(/src\s*=\s*"(.+?)"/)[1];
console.log('Image URL: ' + url);
return url;
}
function makeIconBadge(icon, title) {
// Construct an 'icon badge' which floats to the right of an object
var html = `<span class='fas ${icon} label-right' title='${title}'></span>`;
return html;
}
function makeIconButton(icon, cls, pk, title, options={}) {
// Construct an 'icon button' using the fontawesome set
var classes = `btn btn-default btn-glyph ${cls}`;
var id = `${cls}-${pk}`;
var html = '';
var extraProps = '';
if (options.disabled) {
extraProps += "disabled='true' ";
}
html += `<button pk='${pk}' id='${id}' class='${classes}' title='${title}' ${extraProps}>`;
html += `<span class='fas ${icon}'></span>`;
html += `</button>`;
return html;
}
function makeProgressBar(value, maximum, opts={}) {
/*
* Render a progessbar!
*
* @param value is the current value of the progress bar
* @param maximum is the maximum value of the progress bar
*/
var options = opts || {};
value = parseFloat(value);
var percent = 100;
// Prevent div-by-zero or null value
if (maximum && maximum > 0) {
maximum = parseFloat(maximum);
percent = parseInt(value / maximum * 100);
}
if (percent > 100) {
percent = 100;
}
var extraclass = '';
if (value > maximum) {
extraclass='progress-bar-over';
} else if (value < maximum) {
extraclass = 'progress-bar-under';
}
var style = options.style || '';
var text = '';
if (style == 'percent') {
// Display e.g. "50%"
text = `${percent}%`;
} else if (style == 'max') {
// Display just the maximum value
text = `${maximum}`;
} else if (style == 'value') {
// Display just the current value
text = `${value}`;
} else if (style == 'blank') {
// No display!
text = '';
} else {
/* Default style
* Display e.g. "5 / 10"
*/
text = `${value} / ${maximum}`;
}
var id = options.id || 'progress-bar';
return `
<div id='${id}' class='progress'>
<div class='progress-bar ${extraclass}' role='progressbar' aria-valuenow='${percent}' aria-valuemin='0' aria-valuemax='100' style='width:${percent}%'></div>
<div class='progress-value'>${text}</div>
</div>
`;
}
function enableDragAndDrop(element, url, options) {
/* Enable drag-and-drop file uploading for a given element.
Params:
element - HTML element lookup string e.g. "#drop-div"
url - URL to POST the file to
options - object with following possible values:
label - Label of the file to upload (default='file')
data - Other form data to upload
success - Callback function in case of success
error - Callback function in case of error
method - HTTP method
*/
data = options.data || {};
$(element).on('drop', function(event) {
var transfer = event.originalEvent.dataTransfer;
var label = options.label || 'file';
var formData = new FormData();
// Add the extra data
for (var key in data) {
formData.append(key, data[key]);
}
if (isFileTransfer(transfer)) {
formData.append(label, transfer.files[0]);
inventreeFormDataUpload(
url,
formData,
{
success: function(data, status, xhr) {
console.log('Uploaded file via drag-and-drop');
if (options.success) {
options.success(data, status, xhr);
}
},
error: function(xhr, status, error) {
console.log('File upload failed');
if (options.error) {
options.error(xhr, status, error);
}
},
method: options.method || 'POST',
}
);
} else {
console.log('Ignoring drag-and-drop event (not a file)');
}
});
}
function imageHoverIcon(url) {
/* Render a small thumbnail icon for an image.
* On mouseover, display a full-size version of the image
*/
if (!url) {
url = '/static/img/blank_image.png';
}
var html = `
<a class='hover-icon'>
<img class='hover-img-thumb' src='` + url + `'>
<img class='hover-img-large' src='` + url + `'>
</a>
`;
return html;
}
function inventreeSave(name, value) {
/*
* Save a key:value pair to local storage
*/
var key = "inventree-" + name;
localStorage.setItem(key, value);
}
function inventreeLoad(name, defaultValue) {
/*
* Retrieve a key:value pair from local storage
*/
var key = "inventree-" + name;
var value = localStorage.getItem(key);
if (value == null) {
return defaultValue;
} else {
return value;
}
}
function inventreeLoadInt(name) {
/*
* Retrieve a value from local storage, and attempt to cast to integer
*/
var data = inventreeLoad(name);
return parseInt(data, 10);
}
function inventreeLoadFloat(name) {
var data = inventreeLoad(name);
return parseFloat(data);
}
function inventreeDel(name) {
var key = 'inventree-' + name;
localStorage.removeItem(key);
}

View File

@@ -35,8 +35,8 @@ function loadTree(url, tree, options={}) {
showTags: true,
});
if (sessionStorage.getItem(key)) {
var saved_exp = sessionStorage.getItem(key).split(",");
if (localStorage.getItem(key)) {
var saved_exp = localStorage.getItem(key).split(",");
// Automatically expand the desired notes
for (var q = 0; q < saved_exp.length; q++) {
@@ -57,7 +57,7 @@ function loadTree(url, tree, options={}) {
}
// Save the expanded nodes
sessionStorage.setItem(key, exp);
localStorage.setItem(key, exp);
});
}
},
@@ -106,17 +106,17 @@ function initNavTree(options) {
width: '0px'
}, 50);
sessionStorage.setItem(stateLabel, 'closed');
localStorage.setItem(stateLabel, 'closed');
} else {
sessionStorage.setItem(stateLabel, 'open');
sessionStorage.setItem(widthLabel, `${width}px`);
localStorage.setItem(stateLabel, 'open');
localStorage.setItem(widthLabel, `${width}px`);
}
}
});
}
var state = sessionStorage.getItem(stateLabel);
var width = sessionStorage.getItem(widthLabel) || '300px';
var state = localStorage.getItem(stateLabel);
var width = localStorage.getItem(widthLabel) || '300px';
if (state && state == 'open') {
@@ -131,21 +131,21 @@ function initNavTree(options) {
$(toggleId).click(function() {
var state = sessionStorage.getItem(stateLabel) || 'closed';
var width = sessionStorage.getItem(widthLabel) || '300px';
var state = localStorage.getItem(stateLabel) || 'closed';
var width = localStorage.getItem(widthLabel) || '300px';
if (state == 'open') {
$(treeId).animate({
width: '0px'
}, 50);
sessionStorage.setItem(stateLabel, 'closed');
localStorage.setItem(stateLabel, 'closed');
} else {
$(treeId).animate({
width: width,
}, 50);
sessionStorage.setItem(stateLabel, 'open');
localStorage.setItem(stateLabel, 'open');
}
});
}
@@ -198,17 +198,18 @@ function enableNavbar(options) {
width: '45px'
}, 50);
sessionStorage.setItem(stateLabel, 'closed');
localStorage.setItem(stateLabel, 'closed');
} else {
sessionStorage.setItem(widthLabel, `${width}px`);
sessionStorage.setItem(stateLabel, 'open');
localStorage.setItem(widthLabel, `${width}px`);
localStorage.setItem(stateLabel, 'open');
}
}
});
}
var state = sessionStorage.getItem(stateLabel);
var width = sessionStorage.getItem(widthLabel) || '250px';
var state = localStorage.getItem(stateLabel);
var width = localStorage.getItem(widthLabel) || '250px';
if (state && state == 'open') {
@@ -224,8 +225,8 @@ function enableNavbar(options) {
$(toggleId).click(function() {
var state = sessionStorage.getItem(stateLabel) || 'closed';
var width = sessionStorage.getItem(widthLabel) || '250px';
var state = localStorage.getItem(stateLabel) || 'closed';
var width = localStorage.getItem(widthLabel) || '250px';
if (state == 'open') {
$(navId).animate({
@@ -233,7 +234,7 @@ function enableNavbar(options) {
minWidth: '45px',
}, 50);
sessionStorage.setItem(stateLabel, 'closed');
localStorage.setItem(stateLabel, 'closed');
} else {
@@ -241,7 +242,7 @@ function enableNavbar(options) {
'width': width
}, 50);
sessionStorage.setItem(stateLabel, 'open');
localStorage.setItem(stateLabel, 'open');
}
});
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

After

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -59,6 +59,11 @@
<h1>YOUR COMPONENTS:</h1>
<!-- Autocomplete -->
<h2 class="demoHeaders">Autocomplete</h2>
<div>
<input id="autocomplete" title="type &quot;a&quot;">
</div>
@@ -248,6 +253,23 @@
<!-- Menu -->
<h2 class="demoHeaders">Menu</h2>
<ul style="width:100px;" id="menu">
<li><div>Item 1</div></li>
<li><div>Item 2</div></li>
<li><div>Item 3</div>
<ul>
<li><div>Item 3-1</div></li>
<li><div>Item 3-2</div></li>
<li><div>Item 3-3</div></li>
<li><div>Item 3-4</div></li>
<li><div>Item 3-5</div></li>
</ul>
</li>
<li><div>Item 4</div></li>
<li><div>Item 5</div></li>
</ul>
<!-- Highlight / Error -->
@@ -270,6 +292,33 @@
<script src="jquery-ui.js"></script>
<script>
var availableTags = [
"ActionScript",
"AppleScript",
"Asp",
"BASIC",
"C",
"C++",
"Clojure",
"COBOL",
"ColdFusion",
"Erlang",
"Fortran",
"Groovy",
"Haskell",
"Java",
"JavaScript",
"Lisp",
"Perl",
"PHP",
"Python",
"Ruby",
"Scala",
"Scheme"
];
$( "#autocomplete" ).autocomplete({
source: availableTags
});
@@ -280,6 +329,7 @@
$( "#menu" ).menu();

View File

@@ -1,6 +1,6 @@
/*! jQuery UI - v1.12.1 - 2021-02-23
/*! jQuery UI - v1.12.1 - 2021-07-18
* http://jqueryui.com
* Includes: core.css, resizable.css, theme.css
* Includes: core.css, resizable.css, autocomplete.css, menu.css, theme.css
* To view and modify this theme, visit http://jqueryui.com/themeroller/?scope=&folderName=base&cornerRadiusShadow=8px&offsetLeftShadow=0px&offsetTopShadow=0px&thicknessShadow=5px&opacityShadow=30&bgImgOpacityShadow=0&bgTextureShadow=flat&bgColorShadow=666666&opacityOverlay=30&bgImgOpacityOverlay=0&bgTextureOverlay=flat&bgColorOverlay=aaaaaa&iconColorError=cc0000&fcError=5f3f3f&borderColorError=f1a899&bgTextureError=flat&bgColorError=fddfdf&iconColorHighlight=777620&fcHighlight=777620&borderColorHighlight=dad55e&bgTextureHighlight=flat&bgColorHighlight=fffa90&iconColorActive=ffffff&fcActive=ffffff&borderColorActive=003eff&bgTextureActive=flat&bgColorActive=007fff&iconColorHover=555555&fcHover=2b2b2b&borderColorHover=cccccc&bgTextureHover=flat&bgColorHover=ededed&iconColorDefault=777777&fcDefault=454545&borderColorDefault=c5c5c5&bgTextureDefault=flat&bgColorDefault=f6f6f6&iconColorContent=444444&fcContent=333333&borderColorContent=dddddd&bgTextureContent=flat&bgColorContent=ffffff&iconColorHeader=444444&fcHeader=333333&borderColorHeader=dddddd&bgTextureHeader=flat&bgColorHeader=e9e9e9&cornerRadius=3px&fwDefault=normal&fsDefault=1em&ffDefault=Arial%2CHelvetica%2Csans-serif
* Copyright jQuery Foundation and other contributors; Licensed MIT */
@@ -160,6 +160,66 @@
right: -5px;
top: -5px;
}
.ui-autocomplete {
position: absolute;
top: 0;
left: 0;
cursor: default;
}
.ui-menu {
list-style: none;
padding: 0;
margin: 0;
display: block;
outline: 0;
}
.ui-menu .ui-menu {
position: absolute;
}
.ui-menu .ui-menu-item {
margin: 0;
cursor: pointer;
/* support: IE10, see #8844 */
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
}
.ui-menu .ui-menu-item-wrapper {
position: relative;
padding: 3px 1em 3px .4em;
}
.ui-menu .ui-menu-divider {
margin: 5px 0;
height: 0;
font-size: 0;
line-height: 0;
border-width: 1px 0 0 0;
}
.ui-menu .ui-state-focus,
.ui-menu .ui-state-active {
margin: -1px;
}
/* icon support */
.ui-menu-icons {
position: relative;
}
.ui-menu-icons .ui-menu-item-wrapper {
padding-left: 2em;
}
/* left-aligned */
.ui-menu .ui-icon {
position: absolute;
top: 0;
bottom: 0;
left: .2em;
margin: auto 0;
}
/* right-aligned */
.ui-menu .ui-menu-icon {
left: auto;
right: 0;
}
/* Component containers
----------------------------------*/

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -164,3 +164,63 @@
right: -5px;
top: -5px;
}
.ui-autocomplete {
position: absolute;
top: 0;
left: 0;
cursor: default;
}
.ui-menu {
list-style: none;
padding: 0;
margin: 0;
display: block;
outline: 0;
}
.ui-menu .ui-menu {
position: absolute;
}
.ui-menu .ui-menu-item {
margin: 0;
cursor: pointer;
/* support: IE10, see #8844 */
list-style-image: url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7");
}
.ui-menu .ui-menu-item-wrapper {
position: relative;
padding: 3px 1em 3px .4em;
}
.ui-menu .ui-menu-divider {
margin: 5px 0;
height: 0;
font-size: 0;
line-height: 0;
border-width: 1px 0 0 0;
}
.ui-menu .ui-state-focus,
.ui-menu .ui-state-active {
margin: -1px;
}
/* icon support */
.ui-menu-icons {
position: relative;
}
.ui-menu-icons .ui-menu-item-wrapper {
padding-left: 2em;
}
/* left-aligned */
.ui-menu .ui-icon {
position: absolute;
top: 0;
bottom: 0;
left: .2em;
margin: auto 0;
}
/* right-aligned */
.ui-menu .ui-menu-icon {
left: auto;
right: 0;
}

View File

@@ -1,5 +1,5 @@
/*! jQuery UI - v1.12.1 - 2021-02-23
/*! jQuery UI - v1.12.1 - 2021-07-18
* http://jqueryui.com
* Copyright jQuery Foundation and other contributors; Licensed MIT */
.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}
.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default!important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}

View File

@@ -1,4 +1,4 @@
/*! jQuery UI - v1.12.1 - 2021-02-23
/*! jQuery UI - v1.12.1 - 2021-07-18
* http://jqueryui.com
* Copyright jQuery Foundation and other contributors; Licensed MIT */

File diff suppressed because it is too large Load Diff

View File

@@ -4678,7 +4678,7 @@ input[type="submit"].btn.btn-mini {
.navbar .btn-navbar:active,
.navbar .btn-navbar.active {
background-color: #cccccc \9;
background-color: #ba8;
}
.navbar .btn-navbar .icon-bar {

View File

@@ -6,7 +6,8 @@ import json
import requests
import logging
from datetime import datetime, timedelta
from datetime import timedelta
from django.utils import timezone
from django.core.exceptions import AppRegistryNotReady
from django.db.utils import OperationalError, ProgrammingError
@@ -35,7 +36,7 @@ def schedule_task(taskname, **kwargs):
# If this task is already scheduled, don't schedule it again
# Instead, update the scheduling parameters
if Schedule.objects.filter(func=taskname).exists():
logger.info(f"Scheduled task '{taskname}' already exists - updating!")
logger.debug(f"Scheduled task '{taskname}' already exists - updating!")
Schedule.objects.filter(func=taskname).update(**kwargs)
else:
@@ -51,11 +52,14 @@ def schedule_task(taskname, **kwargs):
pass
def offload_task(taskname, *args, **kwargs):
def offload_task(taskname, force_sync=False, *args, **kwargs):
"""
Create an AsyncTask.
This is different to a 'scheduled' task,
in that it only runs once!
Create an AsyncTask if workers are running.
This is different to a 'scheduled' task,
in that it only runs once!
If workers are not running or force_sync flag
is set then the task is ran synchronously.
"""
try:
@@ -63,10 +67,48 @@ def offload_task(taskname, *args, **kwargs):
except (AppRegistryNotReady):
logger.warning("Could not offload task - app registry not ready")
return
import importlib
from InvenTree.status import is_worker_running
task = AsyncTask(taskname, *args, **kwargs)
if is_worker_running() and not force_sync:
# Running as asynchronous task
try:
task = AsyncTask(taskname, *args, **kwargs)
task.run()
except ImportError:
logger.warning(f"WARNING: '{taskname}' not started - Function not found")
else:
# Split path
try:
app, mod, func = taskname.split('.')
app_mod = app + '.' + mod
except ValueError:
logger.warning(f"WARNING: '{taskname}' not started - Malformed function path")
return
task.run()
# Import module from app
try:
_mod = importlib.import_module(app_mod)
except ModuleNotFoundError:
logger.warning(f"WARNING: '{taskname}' not started - No module named '{app_mod}'")
return
# Retrieve function
try:
_func = getattr(_mod, func)
except AttributeError:
# getattr does not work for local import
_func = None
try:
if not _func:
_func = eval(func)
except NameError:
logger.warning(f"WARNING: '{taskname}' not started - No function named '{func}'")
return
# Workers are not running: run it as synchronous task
_func()
def heartbeat():
@@ -84,7 +126,7 @@ def heartbeat():
except AppRegistryNotReady:
return
threshold = datetime.now() - timedelta(minutes=30)
threshold = timezone.now() - timedelta(minutes=30)
# Delete heartbeat results more than half an hour old,
# otherwise they just create extra noise
@@ -108,7 +150,7 @@ def delete_successful_tasks():
logger.info("Could not perform 'delete_successful_tasks' - App registry not ready")
return
threshold = datetime.now() - timedelta(days=30)
threshold = timezone.now() - timedelta(days=30)
results = Success.objects.filter(
started__lte=threshold
@@ -162,6 +204,25 @@ def check_for_updates():
)
def delete_expired_sessions():
"""
Remove any expired user sessions from the database
"""
try:
from django.contrib.sessions.models import Session
# Delete any sessions that expired more than a day ago
expired = Session.objects.filter(expire_date__lt=timezone.now() - timedelta(days=1))
if True or expired.count() > 0:
logger.info(f"Deleting {expired.count()} expired sessions.")
expired.delete()
except AppRegistryNotReady:
logger.info("Could not perform 'delete_expired_sessions' - App registry not ready")
def update_exchange_rates():
"""
Update currency exchange rates
@@ -213,7 +274,9 @@ def send_email(subject, body, recipients, from_email=None):
offload_task(
'django.core.mail.send_mail',
subject, body,
subject,
body,
from_email,
recipients,
fail_silently=False,
)

View File

@@ -0,0 +1,142 @@
"""
Validate that all URLs specified in template files are correct.
"""
from django.test import TestCase
from django.urls import reverse
import os
import re
from pathlib import Path
class URLTest(TestCase):
# Need fixture data in the database
fixtures = [
'settings',
'build',
'company',
'manufacturer_part',
'price_breaks',
'supplier_part',
'order',
'sales_order',
'bom',
'category',
'params',
'part_pricebreaks',
'part',
'test_templates',
'location',
'stock_tests',
'stock',
'users',
]
def find_files(self, suffix):
"""
Search for all files in the template directories,
which can have URLs rendered
"""
template_dirs = [
('build', 'templates'),
('common', 'templates'),
('company', 'templates'),
('label', 'templates'),
('order', 'templates'),
('part', 'templates'),
('report', 'templates'),
('stock', 'templates'),
('templates', ),
]
template_files = []
here = os.path.abspath(os.path.dirname(__file__))
tld = os.path.join(here, '..')
for directory in template_dirs:
template_dir = os.path.join(tld, *directory)
for path in Path(template_dir).rglob(suffix):
f = os.path.abspath(path)
if f not in template_files:
template_files.append(f)
return template_files
def find_urls(self, input_file):
"""
Search for all instances of {% url %} in supplied template file
"""
urls = []
pattern = "{% url ['\"]([^'\"]+)['\"]([^%]*)%}"
with open(input_file, 'r') as f:
data = f.read()
results = re.findall(pattern, data)
for result in results:
if len(result) == 2:
urls.append([
result[0].strip(),
result[1].strip()
])
elif len(result) == 1:
urls.append([
result[0].strip(),
''
])
return urls
def reverse_url(self, url_pair):
"""
Perform lookup on the URL
"""
url, pk = url_pair
# TODO: Handle reverse lookup of admin URLs!
if url.startswith("admin:"):
return
if pk:
# We will assume that there is at least one item in the database
reverse(url, kwargs={"pk": 1})
else:
reverse(url)
def check_file(self, f):
"""
Run URL checks for the provided file
"""
urls = self.find_urls(f)
for url in urls:
self.reverse_url(url)
def test_html_templates(self):
template_files = self.find_files("*.html")
for f in template_files:
self.check_file(f)
def test_js_templates(self):
template_files = self.find_files("*.js")
for f in template_files:
self.check_file(f)

View File

@@ -39,11 +39,11 @@ from rest_framework.documentation import include_docs_urls
from .views import auth_request
from .views import IndexView, SearchView, DatabaseStatsView
from .views import SettingsView, EditUserView, SetPasswordView
from .views import CurrencySettingsView, CurrencyRefreshView
from .views import CurrencyRefreshView
from .views import AppearanceSelectView, SettingCategorySelectView
from .views import DynamicJsView
from common.views import SettingEdit
from common.views import SettingEdit, UserSettingEdit
from .api import InfoView, NotFoundView
from .api import ActionPluginView
@@ -79,47 +79,48 @@ apipatterns = [
settings_urls = [
url(r'^user/?', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings-user'),
url(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'),
url(r'^i18n/?', include('django.conf.urls.i18n')),
url(r'^global/', SettingsView.as_view(template_name='InvenTree/settings/global.html'), name='settings-global'),
url(r'^report/', SettingsView.as_view(template_name='InvenTree/settings/report.html'), name='settings-report'),
url(r'^category/', SettingCategorySelectView.as_view(), name='settings-category'),
url(r'^part/', SettingsView.as_view(template_name='InvenTree/settings/part.html'), name='settings-part'),
url(r'^stock/', SettingsView.as_view(template_name='InvenTree/settings/stock.html'), name='settings-stock'),
url(r'^build/', SettingsView.as_view(template_name='InvenTree/settings/build.html'), name='settings-build'),
url(r'^purchase-order/', SettingsView.as_view(template_name='InvenTree/settings/po.html'), name='settings-po'),
url(r'^sales-order/', SettingsView.as_view(template_name='InvenTree/settings/so.html'), name='settings-so'),
url(r'^currencies/', CurrencySettingsView.as_view(), name='settings-currencies'),
url(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'),
url(r'^currencies-refresh/', CurrencyRefreshView.as_view(), name='settings-currencies-refresh'),
url(r'^category/', SettingCategorySelectView.as_view(), name='settings-category'),
url(r'^(?P<pk>\d+)/edit/user', UserSettingEdit.as_view(), name='user-setting-edit'),
url(r'^(?P<pk>\d+)/edit/', SettingEdit.as_view(), name='setting-edit'),
# Catch any other urls
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings'),
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/settings.html'), name='settings'),
]
# Some javascript files are served 'dynamically', allowing them to pass through the Django translation layer
# These javascript files are served "dynamically" - i.e. rendered on demand
dynamic_javascript_urls = [
url(r'^api.js', DynamicJsView.as_view(template_name='js/api.js'), name='api.js'),
url(r'^attachment.js', DynamicJsView.as_view(template_name='js/attachment.js'), name='attachment.js'),
url(r'^forms.js', DynamicJsView.as_view(template_name='js/forms.js'), name='forms.js'),
url(r'^model_renderers.js', DynamicJsView.as_view(template_name='js/model_renderers.js'), name='model_renderers.js'),
url(r'^modals.js', DynamicJsView.as_view(template_name='js/modals.js'), name='modals.js'),
url(r'^barcode.js', DynamicJsView.as_view(template_name='js/barcode.js'), name='barcode.js'),
url(r'^bom.js', DynamicJsView.as_view(template_name='js/bom.js'), name='bom.js'),
url(r'^build.js', DynamicJsView.as_view(template_name='js/build.js'), name='build.js'),
url(r'^calendar.js', DynamicJsView.as_view(template_name='js/calendar.js'), name='calendar.js'),
url(r'^company.js', DynamicJsView.as_view(template_name='js/company.js'), name='company.js'),
url(r'^order.js', DynamicJsView.as_view(template_name='js/order.js'), name='order.js'),
url(r'^part.js', DynamicJsView.as_view(template_name='js/part.js'), name='part.js'),
url(r'^label.js', DynamicJsView.as_view(template_name='js/label.js'), name='label.js'),
url(r'^report.js', DynamicJsView.as_view(template_name='js/report.js'), name='report.js'),
url(r'^stock.js', DynamicJsView.as_view(template_name='js/stock.js'), name='stock.js'),
url(r'^tables.js', DynamicJsView.as_view(template_name='js/tables.js'), name='tables.js'),
url(r'^table_filters.js', DynamicJsView.as_view(template_name='js/table_filters.js'), name='table_filters.js'),
url(r'^filters.js', DynamicJsView.as_view(template_name='js/filters.js'), name='filters.js'),
url(r'^inventree.js', DynamicJsView.as_view(template_name='js/dynamic/inventree.js'), name='inventree.js'),
url(r'^calendar.js', DynamicJsView.as_view(template_name='js/dynamic/calendar.js'), name='calendar.js'),
url(r'^nav.js', DynamicJsView.as_view(template_name='js/dynamic/nav.js'), name='nav.js'),
url(r'^settings.js', DynamicJsView.as_view(template_name='js/dynamic/settings.js'), name='settings.js'),
]
# These javascript files are pased through the Django translation layer
translated_javascript_urls = [
url(r'^api.js', DynamicJsView.as_view(template_name='js/translated/api.js'), name='api.js'),
url(r'^attachment.js', DynamicJsView.as_view(template_name='js/translated/attachment.js'), name='attachment.js'),
url(r'^barcode.js', DynamicJsView.as_view(template_name='js/translated/barcode.js'), name='barcode.js'),
url(r'^bom.js', DynamicJsView.as_view(template_name='js/translated/bom.js'), name='bom.js'),
url(r'^build.js', DynamicJsView.as_view(template_name='js/translated/build.js'), name='build.js'),
url(r'^company.js', DynamicJsView.as_view(template_name='js/translated/company.js'), name='company.js'),
url(r'^filters.js', DynamicJsView.as_view(template_name='js/translated/filters.js'), name='filters.js'),
url(r'^forms.js', DynamicJsView.as_view(template_name='js/translated/forms.js'), name='forms.js'),
url(r'^helpers.js', DynamicJsView.as_view(template_name='js/translated/helpers.js'), name='helpers.js'),
url(r'^label.js', DynamicJsView.as_view(template_name='js/translated/label.js'), name='label.js'),
url(r'^model_renderers.js', DynamicJsView.as_view(template_name='js/translated/model_renderers.js'), name='model_renderers.js'),
url(r'^modals.js', DynamicJsView.as_view(template_name='js/translated/modals.js'), name='modals.js'),
url(r'^order.js', DynamicJsView.as_view(template_name='js/translated/order.js'), name='order.js'),
url(r'^part.js', DynamicJsView.as_view(template_name='js/translated/part.js'), name='part.js'),
url(r'^report.js', DynamicJsView.as_view(template_name='js/translated/report.js'), name='report.js'),
url(r'^stock.js', DynamicJsView.as_view(template_name='js/translated/stock.js'), name='stock.js'),
url(r'^tables.js', DynamicJsView.as_view(template_name='js/translated/tables.js'), name='tables.js'),
url(r'^table_filters.js', DynamicJsView.as_view(template_name='js/translated/table_filters.js'), name='table_filters.js'),
]
urlpatterns = [
@@ -128,7 +129,8 @@ urlpatterns = [
url(r'^supplier-part/', include(supplier_part_urls)),
# "Dynamic" javascript files which are rendered using InvenTree templating.
url(r'^dynamic/', include(dynamic_javascript_urls)),
url(r'^js/dynamic/', include(dynamic_javascript_urls)),
url(r'^js/i18n/', include(translated_javascript_urls)),
url(r'^common/', include(common_urls)),

View File

@@ -8,29 +8,49 @@ import re
import common.models
INVENTREE_SW_VERSION = "0.2.5 pre"
INVENTREE_SW_VERSION = "0.5.3"
INVENTREE_API_VERSION = 7
# InvenTree API version
INVENTREE_API_VERSION = 12
"""
Increment thi API version number whenever there is a significant change to the API that any clients need to know about
Increment this API version number whenever there is a significant change to the API that any clients need to know about
v7 -> 2021-07-03
v12 -> 2021-09-07
- Adds API endpoint to receive stock items against a PurchaseOrder
v11 -> 2021-08-26
- Adds "units" field to PartBriefSerializer
- This allows units to be introspected from the "part_detail" field in the StockItem serializer
v10 -> 2021-08-23
- Adds "purchase_price_currency" to StockItem serializer
- Adds "purchase_price_string" to StockItem serializer
- Purchase price is now writable for StockItem serializer
v9 -> 2021-08-09
- Adds "price_string" to part pricing serializers
v8 -> 2021-07-19
- Refactors the API interface for SupplierPart and ManufacturerPart models
- ManufacturerPart objects can no longer be created via the SupplierPart API endpoint
v7 -> 2021-07-03
- Introduced the concept of "API forms" in https://github.com/inventree/InvenTree/pull/1716
- API OPTIONS endpoints provide comprehensive field metedata
- Multiple new API endpoints added for database models
v6 -> 2021-06-23
v6 -> 2021-06-23
- Part and Company images can now be directly uploaded via the REST API
v5 -> 2021-06-21
v5 -> 2021-06-21
- Adds API interface for manufacturer part parameters
v4 -> 2021-06-01
v4 -> 2021-06-01
- BOM items can now accept "variant stock" to be assigned against them
- Many slight API tweaks were needed to get this to work properly!
v3 -> 2021-05-22:
v3 -> 2021-05-22:
- The updated StockItem "history tracking" now uses a different interface
"""
@@ -51,7 +71,7 @@ def inventreeInstanceTitle():
def inventreeVersion():
""" Returns the InvenTree version string """
return INVENTREE_SW_VERSION
return INVENTREE_SW_VERSION.lower().strip()
def inventreeVersionTuple(version=None):
@@ -65,6 +85,28 @@ def inventreeVersionTuple(version=None):
return [int(g) for g in match.groups()]
def isInvenTreeDevelopmentVersion():
"""
Return True if current InvenTree version is a "development" version
"""
return inventreeVersion().endswith('dev')
def inventreeDocsVersion():
"""
Return the version string matching the latest documentation.
Development -> "latest"
Release -> "major.minor.sub" e.g. "0.5.2"
"""
if isInvenTreeDevelopmentVersion():
return "latest"
else:
return INVENTREE_SW_VERSION
def isInvenTreeUpToDate():
"""
Test if the InvenTree instance is "up to date" with the latest version.

View File

@@ -7,11 +7,15 @@ as JSON objects and passing them to modal forms (using jQuery / bootstrap).
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
import json
from django.utils.translation import gettext_lazy as _
from django.template.loader import render_to_string
from django.http import HttpResponse, JsonResponse, HttpResponseRedirect
from django.urls import reverse_lazy
from django.shortcuts import redirect
from django.conf import settings
from django.contrib.auth.mixins import PermissionRequiredMixin
@@ -27,10 +31,8 @@ from stock.models import StockLocation, StockItem
from common.models import InvenTreeSetting, ColorTheme
from users.models import check_user_role, RuleSet
import InvenTree.tasks
from .forms import DeleteForm, EditUserForm, SetPasswordForm
from .forms import ColorThemeSelectForm, SettingCategorySelectForm
from .forms import SettingCategorySelectForm
from .helpers import str2bool
from rest_framework import views
@@ -779,7 +781,7 @@ class SettingsView(TemplateView):
""" View for configuring User settings
"""
template_name = "InvenTree/settings.html"
template_name = "InvenTree/settings/settings.html"
def get_context_data(self, **kwargs):
@@ -787,6 +789,27 @@ class SettingsView(TemplateView):
ctx['settings'] = InvenTreeSetting.objects.all().order_by('key')
ctx["base_currency"] = currency_code_default()
ctx["currencies"] = currency_codes
ctx["rates"] = Rate.objects.filter(backend="InvenTreeExchange")
ctx["categories"] = PartCategory.objects.all().order_by('tree_id', 'lft', 'name')
# When were the rates last updated?
try:
backend = ExchangeBackend.objects.get(name='InvenTreeExchange')
ctx["rates_updated"] = backend.last_update
except:
ctx["rates_updated"] = None
# load locale stats
STAT_FILE = os.path.abspath(os.path.join(settings.BASE_DIR, 'InvenTree/locale_stats.json'))
try:
ctx["locale_stats"] = json.load(open(STAT_FILE, 'r'))
except:
ctx["locale_stats"] = {}
return ctx
@@ -802,46 +825,20 @@ class CurrencyRefreshView(RedirectView):
On a POST request we will attempt to refresh the exchange rates
"""
# Will block for a little bit
InvenTree.tasks.update_exchange_rates()
from InvenTree.tasks import offload_task
return self.get(request, *args, **kwargs)
# Define associated task from InvenTree.tasks list of methods
taskname = 'InvenTree.tasks.update_exchange_rates'
# Run it
offload_task(taskname, force_sync=True)
return redirect(reverse_lazy('settings'))
class CurrencySettingsView(TemplateView):
"""
View for configuring currency settings
"""
template_name = "InvenTree/settings/currencies.html"
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs).copy()
ctx['settings'] = InvenTreeSetting.objects.all().order_by('key')
ctx["base_currency"] = currency_code_default()
ctx["currencies"] = currency_codes
ctx["rates"] = Rate.objects.filter(backend="InvenTreeExchange")
# When were the rates last updated?
try:
backend = ExchangeBackend.objects.get(name='InvenTreeExchange')
ctx["rates_updated"] = backend.last_update
except:
ctx["rates_updated"] = None
return ctx
class AppearanceSelectView(FormView):
class AppearanceSelectView(RedirectView):
""" View for selecting a color theme """
form_class = ColorThemeSelectForm
success_url = reverse_lazy('settings-appearance')
template_name = "InvenTree/settings/appearance.html"
def get_user_theme(self):
""" Get current user color theme """
try:
@@ -851,40 +848,10 @@ class AppearanceSelectView(FormView):
return user_theme
def get_initial(self):
""" Select current user color theme as initial choice """
initial = super(AppearanceSelectView, self).get_initial()
user_theme = self.get_user_theme()
if user_theme:
initial['name'] = user_theme.name
return initial
def get(self, request, *args, **kwargs):
""" Check if current color theme exists, else display alert box """
context = {}
form = self.get_form()
context['form'] = form
user_theme = self.get_user_theme()
if user_theme:
# Check color theme is a valid choice
if not ColorTheme.is_valid_choice(user_theme):
user_color_theme_name = user_theme.name
if not user_color_theme_name:
user_color_theme_name = 'default'
context['invalid_color_theme'] = user_color_theme_name
return self.render_to_response(context)
def post(self, request, *args, **kwargs):
""" Save user color theme selection """
form = self.get_form()
theme = request.POST.get('theme', None)
# Get current user theme
user_theme = self.get_user_theme()
@@ -894,20 +861,10 @@ class AppearanceSelectView(FormView):
user_theme = ColorTheme()
user_theme.user = request.user
if form.is_valid():
theme_selected = form.cleaned_data['name']
user_theme.name = theme
user_theme.save()
# Set color theme to form selection
user_theme.name = theme_selected
user_theme.save()
return self.form_valid(form)
else:
# Set color theme to default
user_theme.name = ColorTheme.default_color_theme[0]
user_theme.save()
return self.form_invalid(form)
return redirect(reverse_lazy('settings'))
class SettingCategorySelectView(FormView):

View File

@@ -5,11 +5,13 @@ JSON API for the Build app
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django_filters.rest_framework import DjangoFilterBackend
from django.conf.urls import url, include
from rest_framework import filters
from rest_framework import generics
from django.conf.urls import url, include
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import rest_framework as rest_filters
from InvenTree.api import AttachmentMixin
from InvenTree.helpers import str2bool, isNull
@@ -19,6 +21,36 @@ from .models import Build, BuildItem, BuildOrderAttachment
from .serializers import BuildAttachmentSerializer, BuildSerializer, BuildItemSerializer
class BuildFilter(rest_filters.FilterSet):
"""
Custom filterset for BuildList API endpoint
"""
status = rest_filters.NumberFilter(label='Status')
active = rest_filters.BooleanFilter(label='Build is active', method='filter_active')
def filter_active(self, queryset, name, value):
if str2bool(value):
queryset = queryset.filter(status__in=BuildStatus.ACTIVE_CODES)
else:
queryset = queryset.exclude(status__in=BuildStatus.ACTIVE_CODES)
return queryset
overdue = rest_filters.BooleanFilter(label='Build is overdue', method='filter_overdue')
def filter_overdue(self, queryset, name, value):
if str2bool(value):
queryset = queryset.filter(Build.OVERDUE_FILTER)
else:
queryset = queryset.exclude(Build.OVERDUE_FILTER)
return queryset
class BuildList(generics.ListCreateAPIView):
""" API endpoint for accessing a list of Build objects.
@@ -28,6 +60,7 @@ class BuildList(generics.ListCreateAPIView):
queryset = Build.objects.all()
serializer_class = BuildSerializer
filterset_class = BuildFilter
filter_backends = [
DjangoFilterBackend,
@@ -35,10 +68,6 @@ class BuildList(generics.ListCreateAPIView):
filters.OrderingFilter,
]
filter_fields = [
'sales_order',
]
ordering_fields = [
'reference',
'part__name',
@@ -47,6 +76,8 @@ class BuildList(generics.ListCreateAPIView):
'target_date',
'completion_date',
'quantity',
'issued_by',
'responsible',
]
search_fields = [
@@ -73,12 +104,33 @@ class BuildList(generics.ListCreateAPIView):
params = self.request.query_params
# exclude parent tree
exclude_tree = params.get('exclude_tree', None)
if exclude_tree is not None:
try:
build = Build.objects.get(pk=exclude_tree)
queryset = queryset.exclude(
pk__in=[bld.pk for bld in build.get_descendants(include_self=True)]
)
except (ValueError, Build.DoesNotExist):
pass
# Filter by "parent"
parent = params.get('parent', None)
if parent is not None:
queryset = queryset.filter(parent=parent)
# Filter by sales_order
sales_order = params.get('sales_order', None)
if sales_order is not None:
queryset = queryset.filter(sales_order=sales_order)
# Filter by "ancestor" builds
ancestor = params.get('ancestor', None)
@@ -95,34 +147,6 @@ class BuildList(generics.ListCreateAPIView):
except (ValueError, Build.DoesNotExist):
pass
# Filter by build status?
status = params.get('status', None)
if status is not None:
queryset = queryset.filter(status=status)
# Filter by "pending" status
active = params.get('active', None)
if active is not None:
active = str2bool(active)
if active:
queryset = queryset.filter(status__in=BuildStatus.ACTIVE_CODES)
else:
queryset = queryset.exclude(status__in=BuildStatus.ACTIVE_CODES)
# Filter by "overdue" status?
overdue = params.get('overdue', None)
if overdue is not None:
overdue = str2bool(overdue)
if overdue:
queryset = queryset.filter(Build.OVERDUE_FILTER)
else:
queryset = queryset.exclude(Build.OVERDUE_FILTER)
# Filter by associated part?
part = params.get('part', None)
@@ -235,6 +259,14 @@ class BuildAttachmentList(generics.ListCreateAPIView, AttachmentMixin):
queryset = BuildOrderAttachment.objects.all()
serializer_class = BuildAttachmentSerializer
filter_backends = [
DjangoFilterBackend,
]
filter_fields = [
'build',
]
class BuildAttachmentDetail(generics.RetrieveUpdateDestroyAPIView, AttachmentMixin):
"""

View File

@@ -0,0 +1,20 @@
# Generated by Django 3.2.4 on 2021-07-08 14:14
import InvenTree.validators
import build.models
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('build', '0029_auto_20210601_1525'),
]
operations = [
migrations.AlterField(
model_name='build',
name='reference',
field=models.CharField(default=build.models.get_next_build_number, help_text='Build Order Reference', max_length=64, unique=True, validators=[InvenTree.validators.validate_build_order_reference], verbose_name='Reference'),
),
]

View File

@@ -21,6 +21,7 @@ from django.core.validators import MinValueValidator
from markdownx.models import MarkdownxField
from mptt.models import MPTTModel, TreeForeignKey
from mptt.exceptions import InvalidMove
from InvenTree.status_codes import BuildStatus, StockStatus, StockHistoryCode
from InvenTree.helpers import increment, getSetting, normalize, MakeBarcode
@@ -37,6 +38,35 @@ from part import models as PartModels
from users import models as UserModels
def get_next_build_number():
"""
Returns the next available BuildOrder reference number
"""
if Build.objects.count() == 0:
return
build = Build.objects.exclude(reference=None).last()
attempts = set([build.reference])
reference = build.reference
while 1:
reference = increment(reference)
if reference in attempts:
# Escape infinite recursion
return reference
if Build.objects.filter(reference=reference).exists():
attempts.add(reference)
else:
break
return reference
class Build(MPTTModel):
""" A Build object organises the creation of new StockItem objects from other existing StockItem objects.
@@ -60,11 +90,28 @@ class Build(MPTTModel):
responsible: User (or group) responsible for completing the build
"""
OVERDUE_FILTER = Q(status__in=BuildStatus.ACTIVE_CODES) & ~Q(target_date=None) & Q(target_date__lte=datetime.now().date())
@staticmethod
def get_api_url():
return reverse('api-build-list')
OVERDUE_FILTER = Q(status__in=BuildStatus.ACTIVE_CODES) & ~Q(target_date=None) & Q(target_date__lte=datetime.now().date())
def api_instance_filters(self):
return {
'parent': {
'exclude_tree': self.pk,
}
}
def save(self, *args, **kwargs):
try:
super().save(*args, **kwargs)
except InvalidMove:
raise ValidationError({
'parent': _('Invalid choice for parent build'),
})
class Meta:
verbose_name = _("Build Order")
@@ -130,6 +177,7 @@ class Build(MPTTModel):
blank=False,
help_text=_('Build Order Reference'),
verbose_name=_('Reference'),
default=get_next_build_number,
validators=[
validate_build_order_reference
]

View File

@@ -10,11 +10,13 @@ from django.db.models import BooleanField
from rest_framework import serializers
from InvenTree.serializers import InvenTreeModelSerializer, InvenTreeAttachmentSerializerField
from InvenTree.serializers import InvenTreeModelSerializer, InvenTreeAttachmentSerializer
from InvenTree.serializers import InvenTreeAttachmentSerializerField, UserSerializerBrief
from stock.serializers import StockItemSerializerBrief
from stock.serializers import LocationSerializer
from part.serializers import PartSerializer, PartBriefSerializer
from users.serializers import OwnerSerializer
from .models import Build, BuildItem, BuildOrderAttachment
@@ -31,6 +33,10 @@ class BuildSerializer(InvenTreeModelSerializer):
overdue = serializers.BooleanField(required=False, read_only=True)
issued_by_detail = UserSerializerBrief(source='issued_by', read_only=True)
responsible_detail = OwnerSerializer(source='responsible', read_only=True)
@staticmethod
def annotate_queryset(queryset):
"""
@@ -57,7 +63,7 @@ class BuildSerializer(InvenTreeModelSerializer):
return queryset
def __init__(self, *args, **kwargs):
part_detail = kwargs.pop('part_detail', False)
part_detail = kwargs.pop('part_detail', True)
super().__init__(*args, **kwargs)
@@ -70,9 +76,12 @@ class BuildSerializer(InvenTreeModelSerializer):
'pk',
'url',
'title',
'batch',
'creation_date',
'completed',
'completion_date',
'destination',
'parent',
'part',
'part_detail',
'overdue',
@@ -82,8 +91,13 @@ class BuildSerializer(InvenTreeModelSerializer):
'status',
'status_text',
'target_date',
'take_from',
'notes',
'link',
'issued_by',
'issued_by_detail',
'responsible',
'responsible_detail',
]
read_only_fields = [
@@ -145,7 +159,7 @@ class BuildItemSerializer(InvenTreeModelSerializer):
]
class BuildAttachmentSerializer(InvenTreeModelSerializer):
class BuildAttachmentSerializer(InvenTreeAttachmentSerializer):
"""
Serializer for a BuildAttachment
"""
@@ -159,6 +173,7 @@ class BuildAttachmentSerializer(InvenTreeModelSerializer):
'pk',
'build',
'attachment',
'filename',
'comment',
'upload_date',
]

View File

@@ -1,101 +0,0 @@
{% extends "build/build_base.html" %}
{% load static %}
{% load i18n %}
{% load inventree_extras %}
{% block page_title %}
{% inventree_title %} | {% trans "Allocate Parts" %}
{% endblock %}
{% block menubar %}
{% include "build/navbar.html" with tab='allocate' %}
{% endblock %}
{% block heading %}
{% trans "Allocate Stock to Build" %}
{% endblock %}
{% block details %}
{% if build.has_untracked_bom_items %}
{% if build.active %}
<div class='btn-group' role='group'>
<button class='btn btn-success' type='button' id='btn-auto-allocate' title='{% trans "Allocate stock to build" %}'>
<span class='fas fa-magic'></span> {% trans "Auto Allocate" %}
</button>
<button class='btn btn-danger' type='button' id='btn-unallocate' title='{% trans "Unallocate stock" %}'>
<span class='fas fa-minus-circle'></span> {% trans "Unallocate Stock" %}
</button>
<!--
<button class='btn btn-primary' type='button' id='btn-order-parts' title='{% trans "Order required parts" %}'>
<span class='fas fa-shopping-cart'></span> {% trans "Order Parts" %}
</button>
-->
</div>
{% if build.areUntrackedPartsFullyAllocated %}
<div class='alert alert-block alert-success'>
{% trans "Untracked stock has been fully allocated for this Build Order" %}
</div>
{% else %}
<div class='alert alert-block alert-danger'>
{% trans "Untracked stock has not been fully allocated for this Build Order" %}
</div>
{% endif %}
{% endif %}
<table class='table table-striped table-condensed' id='allocation-table-untracked'></table>
{% else %}
<div class='alert alert-block alert-info'>
{% trans "This Build Order does not have any associated untracked BOM items" %}
</div>
{% endif %}
{% endblock %}
{% block js_ready %}
{{ block.super }}
var buildInfo = {
pk: {{ build.pk }},
quantity: {{ build.quantity }},
completed: {{ build.completed }},
part: {{ build.part.pk }},
};
{% if build.has_untracked_bom_items %}
// Load allocation table for un-tracked parts
loadBuildOutputAllocationTable(buildInfo, null);
{% endif %}
function reloadTable() {
$('#allocation-table-untracked').bootstrapTable('refresh');
}
{% if build.active %}
$("#btn-auto-allocate").on('click', function() {
launchModalForm(
"{% url 'build-auto-allocate' build.id %}",
{
success: reloadTable,
}
);
});
$('#btn-unallocate').on('click', function() {
launchModalForm(
"{% url 'build-unallocate' build.id %}",
{
success: reloadTable,
}
);
});
$("#btn-order-parts").click(function() {
launchModalForm("/order/purchase-order/order-parts/", {
data: {
build: {{ build.id }},
},
});
});
{% endif %}
{% endblock %}

View File

@@ -1,84 +0,0 @@
{% extends "build/build_base.html" %}
{% load static %}
{% load i18n %}
{% load markdownify %}
{% block menubar %}
{% include "build/navbar.html" with tab='attachments' %}
{% endblock %}
{% block heading %}
{% trans "Attachments" %}
{% endblock %}
{% block details %}
{% include "attachment_table.html" with attachments=build.attachments.all %}
{% endblock %}
{% block js_ready %}
{{ block.super }}
enableDragAndDrop(
'#attachment-dropzone',
'{% url "api-build-attachment-list" %}',
{
data: {
build: {{ build.id }},
},
label: 'attachment',
success: function(data, status, xhr) {
location.reload();
}
}
);
// Callback for creating a new attachment
$('#new-attachment').click(function() {
constructForm('{% url "api-build-attachment-list" %}', {
fields: {
attachment: {},
comment: {},
build: {
value: {{ build.pk }},
hidden: true,
}
},
method: 'POST',
onSuccess: reloadAttachmentTable,
title: '{% trans "Add Attachment" %}',
});
});
loadAttachmentTable(
'{% url "api-build-attachment-list" %}',
{
filters: {
build: {{ build.pk }},
},
onEdit: function(pk) {
var url = `/api/build/attachment/${pk}/`;
constructForm(url, {
fields: {
comment: {},
},
onSuccess: reloadAttachmentTable,
title: '{% trans "Edit Attachment" %}',
});
},
onDelete: function(pk) {
constructForm(`/api/build/attachment/${pk}/`, {
method: 'DELETE',
confirmMessage: '{% trans "Confirm Delete Operation" %}',
title: '{% trans "Delete Attachment" %}',
onSuccess: reloadAttachmentTable,
});
}
}
);
{% endblock %}

View File

@@ -6,7 +6,7 @@
{{ block.super }}
<div class='alert alert-block alert-info'>
<b>{% trans "Automatically Allocate Stock" %}</b><br>
<strong>{% trans "Automatically Allocate Stock" %}</strong><br>
{% trans "The following stock items will be allocated to the specified build output" %}
</div>
{% if allocations %}
@@ -24,7 +24,7 @@
</td>
<td>
{{ item.stock_item.part.full_name }}<br>
<i>{{ item.stock_item.part.description }}</i>
<em>{{ item.stock_item.part.description }}</em>
</td>
<td>{% decimal item.quantity %}</td>
<td>{{ item.stock_item.location }}</td>

View File

@@ -111,8 +111,8 @@ src="{% static 'img/blank_image.png' %}"
<li><a href='#' id='build-cancel'><span class='fas fa-times-circle icon-red'></span> {% trans "Cancel Build" %}</a></li>
{% endif %}
{% if build.status == BuildStatus.CANCELLED and roles.build.delete %}
<li><a href='#' id='build-delete'><span class='fas fa-trash-alt'></span> {% trans "Delete Build"% }</a>
{% endif %}
<li><a href='#' id='build-delete'><span class='fas fa-trash-alt'></span> {% trans "Delete Build" %}</a>
{% endif %}
</ul>
</div>
{% endif %}
@@ -126,7 +126,7 @@ src="{% static 'img/blank_image.png' %}"
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Part" %}</td>
<td><a href="{% url 'part-detail' build.part.id %}">{{ build.part.full_name }}</a></td>
<td><a href="{% url 'part-detail' build.part.id %}?display=build-orders">{{ build.part.full_name }}</a></td>
</tr>
<tr>
<td></td>
@@ -196,10 +196,7 @@ src="{% static 'img/blank_image.png' %}"
});
$("#build-edit").click(function () {
launchModalForm("{% url 'build-edit' build.id %}",
{
reload: true
});
editBuildOrder({{ build.pk }});
});
$("#build-cancel").click(function() {
@@ -241,4 +238,10 @@ src="{% static 'img/blank_image.png' %}"
);
});
attachNavCallbacks({
name: 'buildorder',
default: 'details'
});
{% endblock %}

View File

@@ -1,40 +0,0 @@
{% extends "build/build_base.html" %}
{% load static %}
{% load i18n %}
{% block menubar %}
{% include "build/navbar.html" with tab="children" %}
{% endblock %}
{% block heading %}
{% trans "Child Build Orders" %}
{% endblock %}
{% block details %}
<div id='button-toolbar'>
<div class='button-toolbar container-fluid float-right'>
<div class='filter-list' id='filter-list-sub-build'>
<!-- Empty div for filters -->
</div>
</div>
</div>
<table class='table table-striped table-condensed' id='sub-build-table' data-toolbar='#button-toolbar'></table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
loadBuildTable($('#sub-build-table'), {
url: '{% url "api-build-list" %}',
filterTarget: "#filter-list-sub-build",
params: {
ancestor: {{ build.pk }},
}
});
{% endblock %}

View File

@@ -1,103 +0,0 @@
{% extends "build/build_base.html" %}
{% load static %}
{% load i18n %}
{% block menubar %}
{% include "build/navbar.html" with tab='output' %}
{% endblock %}
{% block content_panels %}
{% if not build.is_complete %}
<div class='panel panel-default panel-inventree'>
<div class='panel-heading'>
<h4>
{% trans "Incomplete Build Outputs" %}
</h4>
</div>
<div class='panel-content'>
<div class='btn-group' role='group'>
{% if build.active %}
<button class='btn btn-primary' type='button' id='btn-create-output' title='{% trans "Create new build output" %}'>
<span class='fas fa-plus-circle'></span> {% trans "Create New Output" %}
</button>
{% endif %}
</div>
{% if build.incomplete_outputs %}
<div class="panel-group" id="build-output-accordion" role="tablist" aria-multiselectable="true">
{% for item in build.incomplete_outputs %}
{% include "build/allocation_card.html" with item=item tracked_items=build.has_tracked_bom_items %}
{% endfor %}
</div>
{% else %}
<div class='alert alert-block alert-info'>
<b>{% trans "Create a new build output" %}</b><br>
{% trans "No incomplete build outputs remain." %}<br>
{% trans "Create a new build output using the button above" %}
</div>
{% endif %}
</div>
</div>
{% endif %}
<div class='panel panel-default panel-inventree'>
<div class='panel-heading'>
<h4>
{% trans "Completed Build Outputs" %}
</h4>
</div>
<div class='panel-content'>
{% include "stock_table.html" with read_only=True %}
</div>
</div>
{% endblock %}
{% block js_ready %}
{{ block.super }}
$('#btn-create-output').click(function() {
launchModalForm('{% url "build-output-create" build.id %}',
{
reload: true,
}
);
});
loadStockTable($("#stock-table"), {
params: {
location_detail: true,
part_detail: true,
build: {{ build.id }},
},
groupByField: 'location',
buttons: [
'#stock-options',
],
url: "{% url 'api-stock-list' %}",
});
var buildInfo = {
pk: {{ build.pk }},
quantity: {{ build.quantity }},
completed: {{ build.completed }},
part: {{ build.part.pk }},
};
{% for item in build.incomplete_outputs %}
// Get the build output as a javascript object
inventreeGet('{% url 'api-stock-detail' item.pk %}', {},
{
success: function(response) {
loadBuildOutputAllocationTable(buildInfo, response);
}
}
);
{% endfor %}
{% endblock %}

View File

@@ -9,7 +9,7 @@
</div>
{% else %}
<div class='alert alert-block alert-danger'>
<b>{% trans "Build Order is incomplete" %}</b><br>
<strong>{% trans "Build Order is incomplete" %}</strong><br>
<ul>
{% if build.incomplete_count > 0 %}
<li>{% trans "Incompleted build outputs remain" %}</li>

View File

@@ -8,7 +8,7 @@
</p>
{% if output %}
<p>
{% blocktrans %}The allocated stock will be installed into the following build output:<br><i>{{output}}</i>{% endblocktrans %}
{% blocktrans %}The allocated stock will be installed into the following build output:<br><em>{{output}}</em>{% endblocktrans %}
</p>
{% endif %}
</div>

View File

@@ -2,142 +2,448 @@
{% load static %}
{% load i18n %}
{% load status_codes %}
{% load markdownify %}
{% block menubar %}
{% include "build/navbar.html" with tab='details' %}
{% include "build/navbar.html" %}
{% endblock %}
{% block heading %}
{% trans "Build Details" %}
{% endblock %}
{% block page_content %}
{% block details %}
<div class='row'>
<div class='col-sm-6'>
<table class='table table-striped'>
<col width='25'>
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Description" %}</td>
<td>{{ build.title }}{% include "clip.html"%}</td>
</tr>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Part" %}</td>
<td><a href="{% url 'part-build' build.part.id %}">{{ build.part.full_name }}</a>{% include "clip.html"%}</td>
</tr>
<tr>
<td></td>
<td>{% trans "Quantity" %}</td><td>{{ build.quantity }}</td>
</tr>
<tr>
<td><span class='fas fa-map-marker-alt'></span></td>
<td>{% trans "Stock Source" %}</td>
<td>
{% if build.take_from %}
<a href="{% url 'stock-location-detail' build.take_from.id %}">{{ build.take_from }}</a>{% include "clip.html"%}
{% else %}
<i>{% trans "Stock can be taken from any available location." %}</i>
{% endif %}
</td>
</tr>
<tr>
<td><span class='fas fa-map-marker-alt'></span></td>
<td>{% trans "Destination" %}</td>
<td>
{% if build.destination %}
<a href="{% url 'stock-location-detail' build.destination.id %}">
{{ build.destination }}
</a>{% include "clip.html"%}
{% else %}
<i>{% trans "Destination location not specified" %}</i>
{% endif %}
</td>
</tr>
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Status" %}</td>
<td>{% build_status_label build.status %}</td>
</tr>
<tr>
<td><span class='fas fa-spinner'></span></td>
<td>{% trans "Progress" %}</td>
<td>{{ build.completed }} / {{ build.quantity }}</td>
</tr>
{% if build.batch %}
<tr>
<td><span class='fas fa-layer-group'></span></td>
<td>{% trans "Batch" %}</td>
<td>{{ build.batch }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if build.parent %}
<tr>
<td><span class='fas fa-sitemap'></span></td>
<td>{% trans "Parent Build" %}</td>
<td><a href="{% url 'build-detail' build.parent.id %}">{{ build.parent }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if build.sales_order %}
<tr>
<td><span class='fas fa-dolly'></span></td>
<td>{% trans "Sales Order" %}</td>
<td><a href="{% url 'so-detail' build.sales_order.id %}">{{ build.sales_order }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if build.link %}
<tr>
<td><span class='fas fa-link'></span></td>
<td>{% trans "External Link" %}</td>
<td><a href="{{ build.link }}">{{ build.link }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if build.issued_by %}
<tr>
<td><span class='fas fa-user'></span></td>
<td>{% trans "Issued By" %}</td>
<td>{{ build.issued_by }}</td>
</tr>
{% endif %}
{% if build.responsible %}
<tr>
<td><span class='fas fa-users'></span></td>
<td>{% trans "Responsible" %}</td>
<td>{{ build.responsible }}</td>
</tr>
{% endif %}
</table>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-details'>
<div class='panel-heading'>
<h4>{% trans "Build Details" %}</h4>
</div>
<div class='col-sm-6'>
<table class='table table-striped'>
<col width='25'>
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Created" %}</td>
<td>{{ build.creation_date }}</td>
</tr>
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Target Date" %}</td>
{% if build.target_date %}
<td>
{{ build.target_date }}{% if build.is_overdue %} <span class='fas fa-calendar-times icon-red'></span>{% endif %}
</td>
{% else %}
<td><i>{% trans "No target date set" %}</i></td>
<div class='panel-content'>
<div class='row'>
<div class='col-sm-6'>
<table class='table table-striped'>
<col width='25'>
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Description" %}</td>
<td>{{ build.title }}{% include "clip.html"%}</td>
</tr>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Part" %}</td>
<td><a href="{% url 'part-detail' build.part.id %}?display=build-orders">{{ build.part.full_name }}</a>{% include "clip.html"%}</td>
</tr>
<tr>
<td></td>
<td>{% trans "Quantity" %}</td><td>{{ build.quantity }}</td>
</tr>
<tr>
<td><span class='fas fa-map-marker-alt'></span></td>
<td>{% trans "Stock Source" %}</td>
<td>
{% if build.take_from %}
<a href="{% url 'stock-location-detail' build.take_from.id %}">{{ build.take_from }}</a>{% include "clip.html"%}
{% else %}
<em>{% trans "Stock can be taken from any available location." %}</em>
{% endif %}
</td>
</tr>
<tr>
<td><span class='fas fa-map-marker-alt'></span></td>
<td>{% trans "Destination" %}</td>
<td>
{% if build.destination %}
<a href="{% url 'stock-location-detail' build.destination.id %}">
{{ build.destination }}
</a>{% include "clip.html"%}
{% else %}
<em>{% trans "Destination location not specified" %}</em>
{% endif %}
</td>
</tr>
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Status" %}</td>
<td>{% build_status_label build.status %}</td>
</tr>
<tr>
<td><span class='fas fa-spinner'></span></td>
<td>{% trans "Progress" %}</td>
<td>{{ build.completed }} / {{ build.quantity }}</td>
</tr>
{% if build.batch %}
<tr>
<td><span class='fas fa-layer-group'></span></td>
<td>{% trans "Batch" %}</td>
<td>{{ build.batch }}{% include "clip.html"%}</td>
</tr>
{% endif %}
</tr>
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Completed" %}</td>
{% if build.completion_date %}
<td>{{ build.completion_date }}{% if build.completed_by %}<span class='badge'>{{ build.completed_by }}</span>{% endif %}</td>
{% else %}
<td><i>{% trans "Build not complete" %}</i></td>
{% if build.parent %}
<tr>
<td><span class='fas fa-sitemap'></span></td>
<td>{% trans "Parent Build" %}</td>
<td><a href="{% url 'build-detail' build.parent.id %}">{{ build.parent }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
</tr>
</table>
{% if build.sales_order %}
<tr>
<td><span class='fas fa-dolly'></span></td>
<td>{% trans "Sales Order" %}</td>
<td><a href="{% url 'so-detail' build.sales_order.id %}">{{ build.sales_order }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if build.link %}
<tr>
<td><span class='fas fa-link'></span></td>
<td>{% trans "External Link" %}</td>
<td><a href="{{ build.link }}">{{ build.link }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if build.issued_by %}
<tr>
<td><span class='fas fa-user'></span></td>
<td>{% trans "Issued By" %}</td>
<td>{{ build.issued_by }}</td>
</tr>
{% endif %}
{% if build.responsible %}
<tr>
<td><span class='fas fa-users'></span></td>
<td>{% trans "Responsible" %}</td>
<td>{{ build.responsible }}</td>
</tr>
{% endif %}
</table>
</div>
<div class='col-sm-6'>
<table class='table table-striped'>
<col width='25'>
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Created" %}</td>
<td>{{ build.creation_date }}</td>
</tr>
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Target Date" %}</td>
{% if build.target_date %}
<td>
{{ build.target_date }}{% if build.is_overdue %} <span class='fas fa-calendar-times icon-red'></span>{% endif %}
</td>
{% else %}
<td><em>{% trans "No target date set" %}</em></td>
{% endif %}
</tr>
<tr>
<td><span class='fas fa-calendar-alt'></span></td>
<td>{% trans "Completed" %}</td>
{% if build.completion_date %}
<td>{{ build.completion_date }}{% if build.completed_by %}<span class='badge'>{{ build.completed_by }}</span>{% endif %}</td>
{% else %}
<td><em>{% trans "Build not complete" %}</em></td>
{% endif %}
</tr>
</table>
</div>
</div>
</div>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-children'>
<div class='panel-heading'>
<h4>{% trans "Child Build Orders" %}</h4>
</div>
<div class='panel-content'>
<div id='child-button-toolbar'>
<div class='button-toolbar container-fluid float-right'>
<div class='filter-list' id='filter-list-sub-build'>
<!-- Empty div for filters -->
</div>
</div>
</div>
<table class='table table-striped table-condensed' id='sub-build-table' data-toolbar='#child-button-toolbar'></table>
</div>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-allocate'>
<div class='panel-heading'>
<h4>{% trans "Allocate Stock to Build" %}</h4>
</div>
<div class='panel-content'>
{% if build.has_untracked_bom_items %}
{% if build.active %}
<div class='btn-group' role='group'>
<button class='btn btn-success' type='button' id='btn-auto-allocate' title='{% trans "Allocate stock to build" %}'>
<span class='fas fa-magic'></span> {% trans "Auto Allocate" %}
</button>
<button class='btn btn-danger' type='button' id='btn-unallocate' title='{% trans "Unallocate stock" %}'>
<span class='fas fa-minus-circle'></span> {% trans "Unallocate Stock" %}
</button>
<!--
<button class='btn btn-primary' type='button' id='btn-order-parts' title='{% trans "Order required parts" %}'>
<span class='fas fa-shopping-cart'></span> {% trans "Order Parts" %}
</button>
-->
</div>
{% if build.areUntrackedPartsFullyAllocated %}
<div class='alert alert-block alert-success'>
{% trans "Untracked stock has been fully allocated for this Build Order" %}
</div>
{% else %}
<div class='alert alert-block alert-danger'>
{% trans "Untracked stock has not been fully allocated for this Build Order" %}
</div>
{% endif %}
{% endif %}
<table class='table table-striped table-condensed' id='allocation-table-untracked'></table>
{% else %}
<div class='alert alert-block alert-info'>
{% trans "This Build Order does not have any associated untracked BOM items" %}
</div>
{% endif %}
</div>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-outputs'>
{% if not build.is_complete %}
<div class='panel-heading'>
<h4>{% trans "Incomplete Build Outputs" %}</h4>
</div>
<div class='panel-content'>
<div class='btn-group' role='group'>
{% if build.active %}
<button class='btn btn-primary' type='button' id='btn-create-output' title='{% trans "Create new build output" %}'>
<span class='fas fa-plus-circle'></span> {% trans "Create New Output" %}
</button>
{% endif %}
</div>
{% if build.incomplete_outputs %}
<div class="panel-group" id="build-output-accordion" role="tablist" aria-multiselectable="true">
{% for item in build.incomplete_outputs %}
{% include "build/allocation_card.html" with item=item tracked_items=build.has_tracked_bom_items %}
{% endfor %}
</div>
{% else %}
<div class='alert alert-block alert-info'>
<strong>{% trans "Create a new build output" %}</strong><br>
{% trans "No incomplete build outputs remain." %}<br>
{% trans "Create a new build output using the button above" %}
</div>
{% endif %}
</div>
{% endif %}
<div class='panel-heading'>
<h4>
{% trans "Completed Build Outputs" %}
</h4>
</div>
<div class='panel-content'>
{% include "stock_table.html" with read_only=True prefix="build-" %}
</div>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-attachments'>
<div class='panel-heading'>
<h4>{% trans "Attachments" %}</h4>
</div>
<div class='panel-content'>
{% include "attachment_table.html" %}
</div>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-notes'>
<div class='panel-heading'>
<div class='row'>
<div class='col-sm-6'>
<h4>{% trans "Build Notes" %}</h4>
</div>
<div class='col-sm-6'>
<div class='btn-group float-right'>
<button type='button' id='edit-notes' title='{% trans "Edit Notes" %}' class='btn btn-small btn-default'>
<span class='fas fa-edit'>
</span>
</button>
</div>
</div>
</div>
</div>
<div class='panel-content'>
{% if build.notes %}
{{ build.notes | markdownify }}
{% endif %}
</div>
</div>
{% endblock %}
{% block js_ready %}
{{ block.super }}
$('#btn-create-output').click(function() {
launchModalForm('{% url "build-output-create" build.id %}',
{
reload: true,
}
);
});
loadStockTable($("#build-stock-table"), {
params: {
location_detail: true,
part_detail: true,
build: {{ build.id }},
is_building: false,
},
groupByField: 'location',
buttons: [
'#stock-options',
],
url: "{% url 'api-stock-list' %}",
});
var buildInfo = {
pk: {{ build.pk }},
quantity: {{ build.quantity }},
completed: {{ build.completed }},
part: {{ build.part.pk }},
};
{% for item in build.incomplete_outputs %}
// Get the build output as a javascript object
inventreeGet('{% url 'api-stock-detail' item.pk %}', {},
{
success: function(response) {
loadBuildOutputAllocationTable(buildInfo, response);
}
}
);
{% endfor %}
loadBuildTable($('#sub-build-table'), {
url: '{% url "api-build-list" %}',
filterTarget: "#filter-list-sub-build",
params: {
ancestor: {{ build.pk }},
}
});
enableDragAndDrop(
'#attachment-dropzone',
'{% url "api-build-attachment-list" %}',
{
data: {
build: {{ build.id }},
},
label: 'attachment',
success: function(data, status, xhr) {
location.reload();
}
}
);
// Callback for creating a new attachment
$('#new-attachment').click(function() {
constructForm('{% url "api-build-attachment-list" %}', {
fields: {
attachment: {},
comment: {},
build: {
value: {{ build.pk }},
hidden: true,
}
},
method: 'POST',
onSuccess: reloadAttachmentTable,
title: '{% trans "Add Attachment" %}',
});
});
loadAttachmentTable(
'{% url "api-build-attachment-list" %}',
{
filters: {
build: {{ build.pk }},
},
onEdit: function(pk) {
var url = `/api/build/attachment/${pk}/`;
constructForm(url, {
fields: {
filename: {},
comment: {},
},
onSuccess: reloadAttachmentTable,
title: '{% trans "Edit Attachment" %}',
});
},
onDelete: function(pk) {
constructForm(`/api/build/attachment/${pk}/`, {
method: 'DELETE',
confirmMessage: '{% trans "Confirm Delete Operation" %}',
title: '{% trans "Delete Attachment" %}',
onSuccess: reloadAttachmentTable,
});
}
}
);
$('#edit-notes').click(function() {
constructForm('{% url "api-build-detail" build.pk %}', {
fields: {
notes: {
multiline: true,
}
},
title: '{% trans "Edit Notes" %}',
reload: true,
});
});
var buildInfo = {
pk: {{ build.pk }},
quantity: {{ build.quantity }},
completed: {{ build.completed }},
part: {{ build.part.pk }},
};
{% if build.has_untracked_bom_items %}
// Load allocation table for un-tracked parts
loadBuildOutputAllocationTable(buildInfo, null);
{% endif %}
function reloadTable() {
$('#allocation-table-untracked').bootstrapTable('refresh');
}
{% if build.active %}
$("#btn-auto-allocate").on('click', function() {
launchModalForm(
"{% url 'build-auto-allocate' build.id %}",
{
success: reloadTable,
}
);
});
$('#btn-unallocate').on('click', function() {
launchModalForm(
"{% url 'build-unallocate' build.id %}",
{
success: reloadTable,
}
);
});
$("#btn-order-parts").click(function() {
launchModalForm("/order/purchase-order/order-parts/", {
data: {
build: {{ build.id }},
},
});
});
{% endif %}
{% endblock %}

View File

@@ -82,7 +82,7 @@
},
{
success: function(response) {
var prefix = '{% settings_value "BUILDORDER_REFERENCE_PREFIX" %}';
var prefix = global_settings.BUILDORDER_REFERENCE_PREFIX;
for (var idx = 0; idx < response.length; idx++) {

View File

@@ -9,46 +9,45 @@
</a>
</li>
<li class='list-group-item {% if tab == "details" %}active{% endif %}' title='{% trans "Build Order Details" %}'>
<a href='{% url "build-detail" build.id %}'>
<li class='list-group-item' title='{% trans "Build Order Details" %}'>
<a href='#' id='select-details' class='nav-toggle'>
<span class='fas fa-info-circle sidebar-icon'></span>
{% trans "Details" %}
</a>
</li>
{% if build.active %}
<li class='list-group-item {% if tab == "allocate" %}active{% endif %}' title='{% trans "Allocate Stock" %}'>
<a href='{% url "build-allocate" build.id %}'>
<li class='list-group-item' title='{% trans "Allocate Stock" %}'>
<a href='#' id='select-allocate' class='nav-toggle'>
<span class='fas fa-tools sidebar-icon'></span>
{% trans "Allocate Stock" %}
</a>
</li>
{% endif %}
<li class='list-group-item {% if tab == "output" %}active{% endif %}' title='{% trans "Build Outputs" %}'>
<a href='{% url "build-output" build.id %}'>
<li class='list-group-item' title='{% trans "Build Outputs" %}'>
<a href='#' id='select-outputs' class='nav-toggle'>
<span class='fas fa-box sidebar-icon'></span>
{% trans "Build Outputs" %}
</a>
</li>
<li class='list-group-item {% if tab == "children" %}active{% endif %}' title='{% trans "Child Build Orders" %}'>
<a href='{% url "build-children" build.id %}'>
<li class='list-group-item' title='{% trans "Child Build Orders" %}'>
<a href='#' id='select-children' class='nav-toggle'>
<span class='fas fa-sitemap sidebar-icon'></span>
{% trans "Child Builds" %}
</a>
</li>
<li class='list-group-item {% if tab == "attachments" %}active{% endif %}' title='{% trans "Attachments" %}'>
<a href='{% url "build-attachments" build.id %}'>
<li class='list-group-item' title='{% trans "Attachments" %}'>
<a href='#' id='select-attachments' class='nav-toggle'>
<span class='fas fa-paperclip sidebar-icon'></span>
{% trans "Attachments" %}
</a>
</li>
<li class='list-group-item {% if tab == "notes" %}active{% endif %}' title='{% trans "Build Order Notes" %}'>
<a href='{% url "build-notes" build.id %}'>
<li class='list-group-item' title='{% trans "Build Order Notes" %}'>
<a href='#' id='select-notes' class='nav-toggle'>
<span class='fas fa-clipboard sidebar-icon'></span>
{% trans "Notes" %}
</a>

View File

@@ -1,49 +0,0 @@
{% extends "build/build_base.html" %}
{% load static %}
{% load i18n %}
{% load markdownify %}
{% block menubar %}
{% include "build/navbar.html" with tab='notes' %}
{% endblock %}
{% block heading %}
{% trans "Build Notes" %}
{% if roles.build.change and not editing %}
<button title='{% trans "Edit notes" %}' class='btn btn-default' id='edit-notes'><span class='fas fa-edit'></span></button>
{% endif %}
{% endblock %}
{% block details %}
{% if editing %}
<hr>
<form method='POST'>
{% csrf_token %}
{{ form }}
<hr>
<button type="submit" class='btn btn-default'>{% trans "Save" %}</button>
</form>
{{ form.media }}
{% else %}
{{ build.notes | markdownify }}
{% endif %}
{% endblock %}
{% block js_ready %}
{{ block.super }}
{% if editing %}
{% else %}
$("#edit-notes").click(function() {
location.href = "{% url 'build-notes' build.id %}?edit=1";
});
{% endif %}
{% endblock %}

View File

@@ -5,11 +5,13 @@ from django.test import TestCase
from django.core.exceptions import ValidationError
from django.db.utils import IntegrityError
from build.models import Build, BuildItem
from stock.models import StockItem
from part.models import Part, BomItem
from InvenTree import status_codes as status
from build.models import Build, BuildItem, get_next_build_number
from part.models import Part, BomItem
from stock.models import StockItem
from stock.tasks import delete_old_stock_items
class BuildTest(TestCase):
"""
@@ -80,8 +82,14 @@ class BuildTest(TestCase):
quantity=2
)
ref = get_next_build_number()
if ref is None:
ref = "0001"
# Create a "Build" object to make 10x objects
self.build = Build.objects.create(
reference=ref,
title="This is a build",
part=self.assembly,
quantity=10
@@ -345,6 +353,11 @@ class BuildTest(TestCase):
# the original BuildItem objects should have been deleted!
self.assertEqual(BuildItem.objects.count(), 0)
self.assertEqual(StockItem.objects.count(), 8)
# Clean up old stock items
delete_old_stock_items()
# New stock items should have been created!
self.assertEqual(StockItem.objects.count(), 7)

View File

@@ -252,36 +252,6 @@ class TestBuildViews(TestCase):
self.assertIn(build.title, content)
def test_build_create(self):
""" Test the build creation view (ajax form) """
url = reverse('build-create')
# Create build without specifying part
response = self.client.get(url, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
# Create build with valid part
response = self.client.get(url, {'part': 1}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
# Create build with invalid part
response = self.client.get(url, {'part': 9999}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertEqual(response.status_code, 200)
def test_build_allocate(self):
""" Test the part allocation view for a Build """
url = reverse('build-allocate', args=(1,))
# Get the page normally
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
# Get the page in editing mode
response = self.client.get(url, {'edit': 1})
self.assertEqual(response.status_code, 200)
def test_build_item_create(self):
""" Test the BuildItem creation view (ajax form) """

View File

@@ -7,37 +7,27 @@ from django.conf.urls import url, include
from . import views
build_detail_urls = [
url(r'^edit/', views.BuildUpdate.as_view(), name='build-edit'),
url(r'^allocate/', views.BuildAllocate.as_view(), name='build-allocate'),
url(r'^cancel/', views.BuildCancel.as_view(), name='build-cancel'),
url(r'^delete/', views.BuildDelete.as_view(), name='build-delete'),
url(r'^create-output/', views.BuildOutputCreate.as_view(), name='build-output-create'),
url(r'^delete-output/', views.BuildOutputDelete.as_view(), name='build-output-delete'),
url(r'^complete-output/?', views.BuildOutputComplete.as_view(), name='build-output-complete'),
url(r'^auto-allocate/?', views.BuildAutoAllocate.as_view(), name='build-auto-allocate'),
url(r'^complete-output/', views.BuildOutputComplete.as_view(), name='build-output-complete'),
url(r'^auto-allocate/', views.BuildAutoAllocate.as_view(), name='build-auto-allocate'),
url(r'^unallocate/', views.BuildUnallocate.as_view(), name='build-unallocate'),
url(r'^complete/', views.BuildComplete.as_view(), name='build-complete'),
url(r'^notes/', views.BuildNotes.as_view(), name='build-notes'),
url(r'^children/', views.BuildDetail.as_view(template_name='build/build_children.html'), name='build-children'),
url(r'^attachments/', views.BuildDetail.as_view(template_name='build/attachments.html'), name='build-attachments'),
url(r'^output/', views.BuildDetail.as_view(template_name='build/build_output.html'), name='build-output'),
url(r'^.*$', views.BuildDetail.as_view(), name='build-detail'),
]
build_urls = [
url(r'item/', include([
url(r'^(?P<pk>\d+)/', include([
url('^edit/?', views.BuildItemEdit.as_view(), name='build-item-edit'),
url('^delete/?', views.BuildItemDelete.as_view(), name='build-item-delete'),
url('^edit/', views.BuildItemEdit.as_view(), name='build-item-edit'),
url('^delete/', views.BuildItemDelete.as_view(), name='build-item-delete'),
])),
url('^new/', views.BuildItemCreate.as_view(), name='build-item-create'),
])),
url(r'new/', views.BuildCreate.as_view(), name='build-create'),
url(r'^(?P<pk>\d+)/', include(build_detail_urls)),
url(r'.*$', views.BuildIndex.as_view(), name='build-index'),

View File

@@ -7,9 +7,8 @@ from __future__ import unicode_literals
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from django.views.generic import DetailView, ListView, UpdateView
from django.views.generic import DetailView, ListView
from django.forms import HiddenInput
from django.urls import reverse
from part.models import Part
from .models import Build, BuildItem
@@ -593,31 +592,6 @@ class BuildOutputComplete(AjaxUpdateView):
}
class BuildNotes(InvenTreeRoleMixin, UpdateView):
""" View for editing the 'notes' field of a Build object.
"""
context_object_name = 'build'
template_name = 'build/notes.html'
model = Build
# Override the default permission role for this View
role_required = 'build.view'
fields = ['notes']
def get_success_url(self):
return reverse('build-notes', kwargs={'pk': self.get_object().id})
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx['editing'] = str2bool(self.request.GET.get('edit', ''))
return ctx
class BuildDetail(InvenTreeRoleMixin, DetailView):
""" Detail view of a single Build object. """
@@ -635,156 +609,15 @@ class BuildDetail(InvenTreeRoleMixin, DetailView):
ctx['BuildStatus'] = BuildStatus
ctx['sub_build_count'] = build.sub_build_count()
return ctx
class BuildAllocate(InvenTreeRoleMixin, DetailView):
""" View for allocating parts to a Build """
model = Build
context_object_name = 'build'
template_name = 'build/allocate.html'
def get_context_data(self, **kwargs):
""" Provide extra context information for the Build allocation page """
context = super(DetailView, self).get_context_data(**kwargs)
build = self.get_object()
part = build.part
bom_items = build.bom_items
context['part'] = part
context['bom_items'] = bom_items
context['has_tracked_bom_items'] = build.has_tracked_bom_items()
context['has_untracked_bom_items'] = build.has_untracked_bom_items()
context['BuildStatus'] = BuildStatus
ctx['part'] = part
ctx['bom_items'] = bom_items
ctx['has_tracked_bom_items'] = build.has_tracked_bom_items()
ctx['has_untracked_bom_items'] = build.has_untracked_bom_items()
context['bom_price'] = build.part.get_price_info(build.quantity, buy=False)
if str2bool(self.request.GET.get('edit', None)):
context['editing'] = True
return context
class BuildCreate(AjaxCreateView):
"""
View to create a new Build object
"""
model = Build
context_object_name = 'build'
form_class = forms.EditBuildForm
ajax_form_title = _('New Build Order')
ajax_template_name = 'modal_form.html'
def get_form(self):
form = super().get_form()
if form['part'].value():
form.fields['part'].widget = HiddenInput()
return form
def get_initial(self):
""" Get initial parameters for Build creation.
If 'part' is specified in the GET query, initialize the Build with the specified Part
"""
initials = super(BuildCreate, self).get_initial().copy()
initials['parent'] = self.request.GET.get('parent', None)
# User has provided a SalesOrder ID
initials['sales_order'] = self.request.GET.get('sales_order', None)
initials['quantity'] = self.request.GET.get('quantity', 1)
part = self.request.GET.get('part', None)
if part:
try:
part = Part.objects.get(pk=part)
# User has provided a Part ID
initials['part'] = part
initials['destination'] = part.get_default_location()
to_order = part.quantity_to_order
if to_order < 1:
to_order = 1
initials['quantity'] = to_order
except (ValueError, Part.DoesNotExist):
pass
initials['reference'] = Build.getNextBuildNumber()
# Pre-fill the issued_by user
initials['issued_by'] = self.request.user
return initials
def get_data(self):
return {
'success': _('Created new build'),
}
def validate(self, build, form, **kwargs):
"""
Perform extra form validation.
- If part is trackable, check that either batch or serial numbers are calculated
By this point form.is_valid() has been executed
"""
pass
class BuildUpdate(AjaxUpdateView):
""" View for editing a Build object """
model = Build
form_class = forms.EditBuildForm
context_object_name = 'build'
ajax_form_title = _('Edit Build Order Details')
ajax_template_name = 'modal_form.html'
def get_form(self):
form = super().get_form()
build = self.get_object()
# Fields which are included in the form, but hidden
hidden = [
'parent',
'sales_order',
]
if build.is_complete:
# Fields which cannot be edited once the build has been completed
hidden += [
'part',
'quantity',
'batch',
'take_from',
'destination',
]
for field in hidden:
form.fields[field].widget = HiddenInput()
return form
def get_data(self):
return {
'info': _('Edited build'),
}
return ctx
class BuildDelete(AjaxDeleteView):

View File

@@ -5,7 +5,7 @@ from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from .models import InvenTreeSetting
from .models import InvenTreeSetting, InvenTreeUserSetting
class SettingsAdmin(ImportExportModelAdmin):
@@ -13,4 +13,10 @@ class SettingsAdmin(ImportExportModelAdmin):
list_display = ('key', 'value')
class UserSettingsAdmin(ImportExportModelAdmin):
list_display = ('key', 'value', 'user', )
admin.site.register(InvenTreeSetting, SettingsAdmin)
admin.site.register(InvenTreeUserSetting, UserSettingsAdmin)

View File

@@ -53,17 +53,20 @@ class FileManager:
ext = os.path.splitext(file.name)[-1].lower().replace('.', '')
if ext in ['csv', 'tsv', ]:
# These file formats need string decoding
raw_data = file.read().decode('utf-8')
# Reset stream position to beginning of file
file.seek(0)
elif ext in ['xls', 'xlsx', 'json', 'yaml', ]:
raw_data = file.read()
# Reset stream position to beginning of file
file.seek(0)
else:
raise ValidationError(_(f'Unsupported file format: {ext.upper()}'))
try:
if ext in ['csv', 'tsv', ]:
# These file formats need string decoding
raw_data = file.read().decode('utf-8')
# Reset stream position to beginning of file
file.seek(0)
elif ext in ['xls', 'xlsx', 'json', 'yaml', ]:
raw_data = file.read()
# Reset stream position to beginning of file
file.seek(0)
else:
raise ValidationError(_(f'Unsupported file format: {ext.upper()}'))
except UnicodeEncodeError:
raise ValidationError(_('Error reading file (invalid encoding)'))
try:
cleaned_data = tablib.Dataset().load(raw_data, format=ext)
@@ -99,13 +102,18 @@ class FileManager:
self.update_headers()
def guess_header(self, header, threshold=80):
""" Try to match a header (from the file) to a list of known headers
"""
Try to match a header (from the file) to a list of known headers
Args:
header - Header name to look for
threshold - Match threshold for fuzzy search
"""
# Replace null values with empty string
if header is None:
header = ''
# Try for an exact match
for h in self.HEADERS:
if h == header:

View File

@@ -0,0 +1,33 @@
# Generated by Django 3.2.4 on 2021-07-22 21:14
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('common', '0010_migrate_currency_setting'),
]
operations = [
migrations.CreateModel(
name='InvenTreeUserSetting',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('value', models.CharField(blank=True, help_text='Settings value', max_length=200)),
('key', models.CharField(help_text='Settings key (must be unique - case insensitive', max_length=50)),
('user', models.ForeignKey(blank=True, help_text='User', null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User')),
],
options={
'verbose_name': 'InvenTree User Setting',
'verbose_name_plural': 'InvenTree User Settings',
},
),
migrations.AddConstraint(
model_name='inventreeusersetting',
constraint=models.UniqueConstraint(fields=('key', 'user'), name='unique key and user'),
),
]

File diff suppressed because it is too large Load Diff

View File

@@ -8,15 +8,19 @@ from __future__ import unicode_literals
from moneyed import CURRENCIES
from django.conf import settings
import common.models
def currency_code_default():
"""
Returns the default currency code (or USD if not specified)
"""
from django.db.utils import ProgrammingError
from common.models import InvenTreeSetting
code = common.models.InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY')
try:
code = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY')
except ProgrammingError:
# database is not initialized yet
code = ''
if code not in CURRENCIES:
code = 'USD'
@@ -42,5 +46,6 @@ def stock_expiry_enabled():
"""
Returns True if the stock expiry feature is enabled
"""
from common.models import InvenTreeSetting
return common.models.InvenTreeSetting.get_setting('STOCK_ENABLE_EXPIRY')
return InvenTreeSetting.get_setting('STOCK_ENABLE_EXPIRY')

View File

@@ -6,9 +6,9 @@
{{ block.super }}
<!--
<p>
<b>{{ name }}</b><br>
<strong>{{ name }}</strong><br>
{{ description }}<br>
<i>{% trans "Current value" %}: {{ value }}</i>
<em>{% trans "Current value" %}: {{ value }}</em>
</p>
-->
{% endblock %}

View File

@@ -45,11 +45,28 @@ class SettingEdit(AjaxUpdateView):
ctx['key'] = setting.key
ctx['value'] = setting.value
ctx['name'] = models.InvenTreeSetting.get_setting_name(setting.key)
ctx['description'] = models.InvenTreeSetting.get_setting_description(setting.key)
ctx['name'] = self.model.get_setting_name(setting.key)
ctx['description'] = self.model.get_setting_description(setting.key)
return ctx
def get_data(self):
"""
Custom data to return to the client after POST success
"""
data = {}
setting = self.get_object()
data['pk'] = setting.pk
data['key'] = setting.key
data['value'] = setting.value
data['is_bool'] = setting.is_bool()
data['is_int'] = setting.is_int()
return data
def get_form(self):
"""
Override default get_form behaviour
@@ -69,12 +86,12 @@ class SettingEdit(AjaxUpdateView):
self.object.value = str2bool(setting.value)
form.fields['value'].value = str2bool(setting.value)
name = models.InvenTreeSetting.get_setting_name(setting.key)
name = self.model.get_setting_name(setting.key)
if name:
form.fields['value'].label = name
description = models.InvenTreeSetting.get_setting_description(setting.key)
description = self.model.get_setting_description(setting.key)
if description:
form.fields['value'].help_text = description
@@ -111,6 +128,18 @@ class SettingEdit(AjaxUpdateView):
form.add_error('value', _('Supplied value must be a boolean'))
class UserSettingEdit(SettingEdit):
"""
View for editing an InvenTree key:value user settings object,
(or creating it if the key does not already exist)
"""
model = models.InvenTreeUserSetting
ajax_form_title = _('Change User Setting')
form_class = forms.SettingEditForm
ajax_template_name = "common/edit_setting.html"
class MultiStepFormView(SessionWizardView):
""" Setup basic methods of multi-step form

View File

@@ -6,6 +6,8 @@ Provides a JSON API for the Company app
from __future__ import unicode_literals
from django_filters.rest_framework import DjangoFilterBackend
from django_filters import rest_framework as rest_filters
from rest_framework import filters
from rest_framework import generics
@@ -84,6 +86,23 @@ class CompanyDetail(generics.RetrieveUpdateDestroyAPIView):
return queryset
class ManufacturerPartFilter(rest_filters.FilterSet):
"""
Custom API filters for the ManufacturerPart list endpoint.
"""
class Meta:
model = ManufacturerPart
fields = [
'manufacturer',
'MPN',
'part',
]
# Filter by 'active' status of linked part
active = rest_filters.BooleanFilter(field_name='part__active')
class ManufacturerPartList(generics.ListCreateAPIView):
""" API endpoint for list view of ManufacturerPart object
@@ -98,6 +117,7 @@ class ManufacturerPartList(generics.ListCreateAPIView):
)
serializer_class = ManufacturerPartSerializer
filterset_class = ManufacturerPartFilter
def get_serializer(self, *args, **kwargs):
@@ -115,49 +135,17 @@ class ManufacturerPartList(generics.ListCreateAPIView):
return self.serializer_class(*args, **kwargs)
def filter_queryset(self, queryset):
"""
Custom filtering for the queryset.
"""
queryset = super().filter_queryset(queryset)
params = self.request.query_params
# Filter by manufacturer
manufacturer = params.get('manufacturer', None)
if manufacturer is not None:
queryset = queryset.filter(manufacturer=manufacturer)
# Filter by parent part?
part = params.get('part', None)
if part is not None:
queryset = queryset.filter(part=part)
# Filter by 'active' status of the part?
active = params.get('active', None)
if active is not None:
active = str2bool(active)
queryset = queryset.filter(part__active=active)
return queryset
filter_backends = [
DjangoFilterBackend,
filters.SearchFilter,
filters.OrderingFilter,
]
filter_fields = [
]
search_fields = [
'manufacturer__name',
'description',
'MPN',
'part__IPN',
'part__name',
'part__description',
]
@@ -262,11 +250,7 @@ class SupplierPartList(generics.ListCreateAPIView):
- POST: Create a new SupplierPart object
"""
queryset = SupplierPart.objects.all().prefetch_related(
'part',
'supplier',
'manufacturer_part__manufacturer',
)
queryset = SupplierPart.objects.all()
def get_queryset(self):
@@ -355,6 +339,7 @@ class SupplierPartList(generics.ListCreateAPIView):
'manufacturer_part__manufacturer__name',
'description',
'manufacturer_part__MPN',
'part__IPN',
'part__name',
'part__description',
]

View File

@@ -6,14 +6,12 @@ Django Forms for interacting with Company app
from __future__ import unicode_literals
from InvenTree.forms import HelperForm
from InvenTree.fields import InvenTreeMoneyField, RoundingDecimalFormField
from InvenTree.fields import RoundingDecimalFormField
from django.utils.translation import ugettext_lazy as _
import django.forms
from .models import Company
from .models import ManufacturerPart
from .models import SupplierPart
from .models import SupplierPriceBreak
@@ -35,86 +33,6 @@ class CompanyImageDownloadForm(HelperForm):
]
class EditManufacturerPartForm(HelperForm):
""" Form for editing a ManufacturerPart object """
field_prefix = {
'link': 'fa-link',
'MPN': 'fa-hashtag',
}
class Meta:
model = ManufacturerPart
fields = [
'part',
'manufacturer',
'MPN',
'description',
'link',
]
class EditSupplierPartForm(HelperForm):
""" Form for editing a SupplierPart object """
field_prefix = {
'link': 'fa-link',
'SKU': 'fa-hashtag',
'note': 'fa-pencil-alt',
}
single_pricing = InvenTreeMoneyField(
label=_('Single Price'),
help_text=_('Single quantity price'),
decimal_places=4,
max_digits=19,
required=False,
)
manufacturer = django.forms.ChoiceField(
required=False,
help_text=_('Select manufacturer'),
choices=[],
)
MPN = django.forms.CharField(
required=False,
help_text=_('Manufacturer Part Number'),
max_length=100,
label=_('MPN'),
)
class Meta:
model = SupplierPart
fields = [
'part',
'supplier',
'SKU',
'manufacturer',
'MPN',
'description',
'link',
'note',
'single_pricing',
# 'base_cost',
# 'multiple',
'packaging',
]
def get_manufacturer_choices(self):
""" Returns tuples for all manufacturers """
empty_choice = [('', '----------')]
manufacturers = [(manufacturer.id, manufacturer.name) for manufacturer in Company.objects.filter(is_manufacturer=True)]
return empty_choice + manufacturers
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['manufacturer'].choices = self.get_manufacturer_choices()
class EditPriceBreakForm(HelperForm):
""" Form for creating / editing a supplier price break """

View File

@@ -0,0 +1,17 @@
# Generated by Django 3.2.5 on 2021-10-04 20:41
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('company', '0040_alter_company_currency'),
]
operations = [
migrations.AlterModelOptions(
name='company',
options={'ordering': ['name'], 'verbose_name_plural': 'Companies'},
),
]

View File

@@ -10,8 +10,8 @@ import os
from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator
from django.core.exceptions import ValidationError
from django.db import models
from django.db.utils import IntegrityError
from django.db.models import Sum, Q, UniqueConstraint
from django.apps import apps
@@ -94,6 +94,7 @@ class Company(models.Model):
constraints = [
UniqueConstraint(fields=['name', 'email'], name='unique_name_email_pair')
]
verbose_name_plural = "Companies"
name = models.CharField(max_length=100, blank=False,
help_text=_('Company name'),
@@ -430,6 +431,22 @@ class ManufacturerPartParameter(models.Model):
)
class SupplierPartManager(models.Manager):
""" Define custom SupplierPart objects manager
The main purpose of this manager is to improve database hit as the
SupplierPart model involves A LOT of foreign keys lookups
"""
def get_queryset(self):
# Always prefetch related models
return super().get_queryset().prefetch_related(
'part',
'supplier',
'manufacturer_part__manufacturer',
)
class SupplierPart(models.Model):
""" Represents a unique part as provided by a Supplier
Each SupplierPart is identified by a SKU (Supplier Part Number)
@@ -450,6 +467,8 @@ class SupplierPart(models.Model):
packaging: packaging that the part is supplied in, e.g. "Reel"
"""
objects = SupplierPartManager()
@staticmethod
def get_api_url():
return reverse('api-supplier-part-list')
@@ -457,56 +476,13 @@ class SupplierPart(models.Model):
def get_absolute_url(self):
return reverse('supplier-part-detail', kwargs={'pk': self.id})
def save(self, *args, **kwargs):
""" Overriding save method to process the linked ManufacturerPart
"""
if 'manufacturer' in kwargs:
manufacturer_id = kwargs.pop('manufacturer')
try:
manufacturer = Company.objects.get(pk=int(manufacturer_id))
except (ValueError, Company.DoesNotExist):
manufacturer = None
else:
manufacturer = None
if 'MPN' in kwargs:
MPN = kwargs.pop('MPN')
else:
MPN = None
if manufacturer or MPN:
if not self.manufacturer_part:
# Create ManufacturerPart
manufacturer_part = ManufacturerPart.create(part=self.part,
manufacturer=manufacturer,
mpn=MPN,
description=self.description)
self.manufacturer_part = manufacturer_part
else:
# Update ManufacturerPart (if ID exists)
try:
manufacturer_part_id = self.manufacturer_part.id
except AttributeError:
manufacturer_part_id = None
if manufacturer_part_id:
try:
(manufacturer_part, created) = ManufacturerPart.objects.update_or_create(part=self.part,
manufacturer=manufacturer,
MPN=MPN)
except IntegrityError:
manufacturer_part = None
raise ValidationError(f'ManufacturerPart linked to {self.part} from manufacturer {manufacturer.name}'
f'with part number {MPN} already exists!')
if manufacturer_part:
self.manufacturer_part = manufacturer_part
self.clean()
self.validate_unique()
super().save(*args, **kwargs)
def api_instance_filters(self):
return {
'manufacturer_part': {
'part': self.part.pk
}
}
class Meta:
unique_together = ('part', 'supplier', 'SKU')
@@ -514,6 +490,46 @@ class SupplierPart(models.Model):
# This model was moved from the 'Part' app
db_table = 'part_supplierpart'
def clean(self):
super().clean()
# Ensure that the linked manufacturer_part points to the same part!
if self.manufacturer_part and self.part:
if not self.manufacturer_part.part == self.part:
raise ValidationError({
'manufacturer_part': _("Linked manufacturer part must reference the same base part"),
})
def save(self, *args, **kwargs):
""" Overriding save method to connect an existing ManufacturerPart """
manufacturer_part = None
if all(key in kwargs for key in ('manufacturer', 'MPN')):
manufacturer_name = kwargs.pop('manufacturer')
MPN = kwargs.pop('MPN')
# Retrieve manufacturer part
try:
manufacturer_part = ManufacturerPart.objects.get(manufacturer__name=manufacturer_name, MPN=MPN)
except (ValueError, Company.DoesNotExist):
# ManufacturerPart does not exist
pass
if manufacturer_part:
if not self.manufacturer_part:
# Connect ManufacturerPart to SupplierPart
self.manufacturer_part = manufacturer_part
else:
raise ValidationError(f'SupplierPart {self.__str__} is already linked to {self.manufacturer_part}')
self.clean()
self.validate_unique()
super().save(*args, **kwargs)
part = models.ForeignKey('part.Part', on_delete=models.CASCADE,
related_name='supplier_parts',
verbose_name=_('Base Part'),

View File

@@ -96,7 +96,9 @@ class CompanySerializer(InvenTreeModelSerializer):
class ManufacturerPartSerializer(InvenTreeModelSerializer):
""" Serializer for ManufacturerPart object """
"""
Serializer for ManufacturerPart object
"""
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
@@ -106,8 +108,8 @@ class ManufacturerPartSerializer(InvenTreeModelSerializer):
def __init__(self, *args, **kwargs):
part_detail = kwargs.pop('part_detail', False)
manufacturer_detail = kwargs.pop('manufacturer_detail', False)
part_detail = kwargs.pop('part_detail', True)
manufacturer_detail = kwargs.pop('manufacturer_detail', True)
prettify = kwargs.pop('pretty', False)
super(ManufacturerPartSerializer, self).__init__(*args, **kwargs)
@@ -202,28 +204,31 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
supplier = serializers.PrimaryKeyRelatedField(queryset=Company.objects.filter(is_supplier=True))
manufacturer = serializers.PrimaryKeyRelatedField(source='manufacturer_part.manufacturer', read_only=True)
manufacturer = serializers.CharField(read_only=True)
MPN = serializers.StringRelatedField(source='manufacturer_part.MPN')
MPN = serializers.CharField(read_only=True)
manufacturer_part = ManufacturerPartSerializer(read_only=True)
manufacturer_part_detail = ManufacturerPartSerializer(source='manufacturer_part', read_only=True)
class Meta:
model = SupplierPart
fields = [
'description',
'link',
'manufacturer',
'manufacturer_detail',
'manufacturer_part',
'manufacturer_part_detail',
'MPN',
'note',
'pk',
'packaging',
'part',
'part_detail',
'pretty_name',
'SKU',
'supplier',
'supplier_detail',
'SKU',
'manufacturer',
'MPN',
'manufacturer_detail',
'manufacturer_part',
'description',
'link',
]
def create(self, validated_data):
@@ -233,12 +238,12 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
supplier_part = super().create(validated_data)
# Get ManufacturerPart raw data (unvalidated)
manufacturer_id = self.initial_data.get('manufacturer', None)
manufacturer = self.initial_data.get('manufacturer', None)
MPN = self.initial_data.get('MPN', None)
if manufacturer_id and MPN:
if manufacturer and MPN:
kwargs = {
'manufacturer': manufacturer_id,
'manufacturer': manufacturer,
'MPN': MPN,
}
supplier_part.save(**kwargs)

View File

@@ -1,38 +0,0 @@
{% extends "company/company_base.html" %}
{% load static %}
{% load i18n %}
{% block menubar %}
{% include "company/navbar.html" with tab="assigned" %}
{% endblock %}
{% block heading %}
{% trans "Assigned Stock" %}
{% endblock %}
{% block details %}
<div id='button-toolbar'>
<div class='filter-list' id='filter-list-stock'>
<!-- An empty div in which the filter list will be constructed -->
</div>
</div>
<table class='table table-striped table-condensed' id='stock-table' data-toolbar='#button-toolbar'></table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
loadStockTable($("#stock-table"), {
params: {
customer: {{ company.id }},
part_detail: true,
location_detail: true,
},
url: "{% url 'api-stock-list' %}",
filterKey: "customerstock",
});
{% endblock %}

View File

@@ -71,6 +71,17 @@
<td><a href="{{ company.website }}">{{ company.website }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-dollar-sign'></span></td>
<td>{% trans "Currency" %}</td>
<td>
{% if company.currency %}
{{ company.currency }}
{% else %}
<em>{% trans "Uses default currency" %}</em>
{% endif %}
</td>
</tr>
{% if company.address %}
<tr>
<td><span class='fas fa-map-marked-alt'></span></td>
@@ -99,6 +110,22 @@
<td>{{ company.contact }}{% include "clip.html"%}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-industry'></span></td>
<td>{%trans "Manufacturer" %}</td>
<td>{% include "yesnolabel.html" with value=company.is_manufacturer %}</td>
</tr>
<tr>
<td><span class='fas fa-building'></span></td>
<td>{% trans "Supplier" %}</td>
<td>{% include 'yesnolabel.html' with value=company.is_supplier %}</td>
</tr>
<tr>
<td><span class='fas fa-user-tie'></span></td>
<td>{% trans "Customer" %}</td>
<td>{% include 'yesnolabel.html' with value=company.is_customer %}</td>
</tr>
</table>
{% endblock %}
@@ -171,17 +198,16 @@
);
});
{% settings_value "INVENTREE_DOWNLOAD_FROM_URL" as allow_download %}
if (global_settings.INVENTREE_DOWNLOAD_FROM_URL) {
{% if allow_download %}
$('#company-image-url').click(function() {
launchModalForm(
'{% url "company-image-download" company.id %}',
{
reload: true,
}
)
});
{% endif %}
$('#company-image-url').click(function() {
launchModalForm(
'{% url "company-image-download" company.id %}',
{
reload: true,
}
)
});
}
{% endblock %}

View File

@@ -1,79 +1,405 @@
{% extends "company/company_base.html" %}
{% load static %}
{% load i18n %}
{% load markdownify %}
{% block menubar %}
{% include 'company/navbar.html' with tab='details' %}
{% endblock %}
{% block heading %}
{% trans "Company Details" %}
{% endblock %}
{% block page_content %}
{% block details %}
<div class='row'>
<div class='col-sm-6'>
<table class='table table-striped'>
<col width='25'>
<col>
<tr>
<td><span class='fas fa-font'></span></td>
<td>{% trans "Company Name" %}</td>
<td>{{ company.name }}{% include "clip.html"%}</td>
</tr>
{% if company.description %}
<tr>
<td><span class='fas fa-info'></span></td>
<td>{% trans "Description" %}</td>
<td>{{ company.description }}{% include "clip.html"%}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-globe'></span></td>
<td>{% trans "Website" %}</td>
<td>
{% if company.website %}<a href='{{ company.website }}'>{{ company.website }}</a>{% include "clip.html"%}
{% else %}<i>{% trans "No website specified" %}</i>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-supplier-parts'>
<div class='panel-heading'>
<h4>{% trans "Supplier Parts" %}</h4>
</div>
<div class='panel-content'>
{% if roles.purchase_order.change %}
<div id='supplier-part-button-toolbar'>
<div class='button-toolbar container-fluid'>
<div class='btn-group' role='group'>
{% if roles.purchase_order.add %}
<button class="btn btn-success" id='supplier-part-create' title='{% trans "Create new supplier part" %}'>
<span class='fas fa-plus-circle'></span> {% trans "New Supplier Part" %}
</button>
{% endif %}
</td>
</tr>
<tr>
<td><span class='fas fa-dollar-sign'></span></td>
<td>{% trans "Currency" %}</td>
<td>
{% if company.currency %}{{ company.currency }}
{% else %}<i>{% trans "Uses default currency" %}</i>
{% endif %}
</td>
</tr>
<div class='btn-group'>
<button class="btn btn-primary dropdown-toggle" id='supplier-table-options' type="button" data-toggle="dropdown">{% trans "Options" %}
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{% if roles.purchase_order.add %}
<li><a href='#' id='multi-supplier-part-order' title='{% trans "Order parts" %}'>{% trans "Order Parts" %}</a></li>
{% endif %}
{% if roles.purchase_order.delete %}
<li><a href='#' id='multi-supplier-part-delete' title='{% trans "Delete parts" %}'>{% trans "Delete Parts" %}</a></li>
{% endif %}
</ul>
</div>
</div>
<div class='filter-list' id='filter-list-supplier-part'>
<!-- Empty div (will be filled out with available BOM filters) -->
</div>
</div>
</div>
{% endif %}
<table class='table table-striped table-condensed' id='supplier-part-table' data-toolbar='#supplier-part-button-toolbar'>
</table>
</div>
<div class='col-sm-6'>
<table class='table table-striped'>
<col width='25'>
<col>
<tr>
<td><span class='fas fa-industry'></span></td>
<td>{% trans "Manufacturer" %}</td>
<td>{% include "yesnolabel.html" with value=company.is_manufacturer %}</td>
</tr>
<tr>
<td><span class='fas fa-building'></span></td>
<td>{% trans "Supplier" %}</td>
<td>{% include 'yesnolabel.html' with value=company.is_supplier %}</td>
</tr>
<tr>
<td><span class='fas fa-user-tie'></span></td>
<td>{% trans "Customer" %}</td>
<td>{% include 'yesnolabel.html' with value=company.is_customer %}</td>
</tr>
</table>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-manufacturer-parts'>
<div class='panel-heading'>
<h4>{% trans "Manufacturer Parts" %}</h4>
</div>
<div class='panel-content'>
{% if roles.purchase_order.change %}
<div id='manufacturer-part-button-toolbar'>
<div class='button-toolbar container-fluid'>
<div class='btn-group' role='group'>
{% if roles.purchase_order.add %}
<button type="button" class="btn btn-success" id='manufacturer-part-create' title='{% trans "Create new manufacturer part" %}'>
<span class='fas fa-plus-circle'></span> {% trans "New Manufacturer Part" %}
</button>
{% endif %}
<div class='btn-group' role='group'>
<button class="btn btn-primary dropdown-toggle" id='manufacturer-table-options' type="button" data-toggle="dropdown">{% trans "Options" %}
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{% if roles.purchase_order.add %}
<li><a href='#' id='multi-manufacturer-part-order' title='{% trans "Order parts" %}'>{% trans "Order Parts" %}</a></li>
{% endif %}
{% if roles.purchase_order.delete %}
<li><a href='#' id='multi-manufacturer-part-delete' title='{% trans "Delete parts" %}'>{% trans "Delete Parts" %}</a></li>
{% endif %}
</ul>
</div>
</div>
<div class='filter-list' id='filter-list-supplier-part'>
<!-- Empty div (will be filled out with available BOM filters) -->
</div>
</div>
</div>
{% endif %}
<table class='table table-striped table-condensed' id='manufacturer-part-table' data-toolbar='#manufacturer-part-button-toolbar'>
</table>
</div>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-company-stock'>
<div class='panel-heading'>
<h4>{% trans "Supplier Stock" %}</h4>
</div>
<div class='panel-content'>
{% include "stock_table.html" %}
</div>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-purchase-orders'>
<div class='panel-heading'>
<h4>{% trans "Purchase Orders" %}</h4>
</div>
<div class='panel-content'>
{% if roles.purchase_order.add %}
<div id='po-button-bar'>
<div class='button-toolbar container-fluid' style='float: right;'>
<button class='btn btn-success' type='button' id='company-order2' title='{% trans "Create new purchase order" %}'>
<span class='fas fa-plus-circle'></span> {% trans "New Purchase Order" %}</button>
<div class='filter-list' id='filter-list-purchaseorder'>
<!-- Empty div -->
</div>
</div>
</div>
{% endif %}
<table class='table table-striped table-condensed po-table' id='purchase-order-table' data-toolbar='#po-button-bar'>
</table>
</div>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-sales-orders'>
<div class='panel-heading'>
<h4>{% trans "Sales Orders" %}</h4>
</div>
<div class='panel-content'>
{% if roles.sales_order.add %}
<div id='so-button-bar'>
<div class='button-toolbar container-fluid' style='float: right;'>
<button class='btn btn-success' type='button' id='new-sales-order' title='{% trans "Create new sales order" %}'>
<div class='fas fa-plus-circle'></div> {% trans "New Sales Order" %}
</button>
<div class='filter-list' id='filter-list-salesorder'>
<!-- Empty div -->
</div>
</div>
</div>
{% endif %}
<table class='table table-striped table-condensed po-table' id='sales-order-table' data-toolbar='#so-button-bar'>
</table>
</div>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-assigned-stock'>
<div class='panel-heading'>
<h4>{% trans "Assigned Stock" %}</h4>
</div>
<div class='panel-content'>
<div id='assigned-stock-button-toolbar'>
<div class='filter-list' id='filter-list-stock'>
<!-- An empty div in which the filter list will be constructed -->
</div>
</div>
<table class='table table-striped table-condensed' id='assigned-stock-table' data-toolbar='#bassigned-stock-utton-toolbar'></table>
</div>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-company-notes'>
<div class='panel-heading'>
<div class='row'>
<div class='col-sm-6'>
<h4>{% trans "Company Notes" %}</h4>
</div>
<div class='col-sm-6'>
<div class='btn-group float-right'>
<button type='button' id='edit-notes' title='{% trans "Edit Notes" %}' class='btn btn-small btn-default'>
<span class='fas fa-edit'>
</span>
</button>
</div>
</div>
</div>
</div>
<div class='panel-content'>
{% if company.notes %}
{{ company.notes | markdownify }}
{% endif %}
</div>
</div>
{% endblock %}
{% block js_ready %}
{{ block.super }}
$('#edit-notes').click(function() {
constructForm('{% url "api-company-detail" company.pk %}', {
fields: {
notes: {
multiline: true,
}
},
title: '{% trans "Edit Notes" %}',
reload: true,
});
});
loadStockTable($("#assigned-stock-table"), {
params: {
customer: {{ company.id }},
part_detail: true,
location_detail: true,
},
url: "{% url 'api-stock-list' %}",
filterKey: "customerstock",
});
{% if company.is_customer %}
loadSalesOrderTable("#sales-order-table", {
url: "{% url 'api-so-list' %}",
params: {
customer: {{ company.id }},
}
});
$("#new-sales-order").click(function() {
createSalesOrder({
customer: {{ company.pk }},
});
});
{% endif %}
{% if company.is_supplier %}
loadPurchaseOrderTable("#purchase-order-table", {
url: "{% url 'api-po-list' %}",
params: {
supplier: {{ company.id }},
}
});
function newOrder() {
createPurchaseOrder({
supplier: {{ company.pk }},
});
}
$("#company-order").click(function() {
newOrder();
});
$("#company-order2").click(function() {
newOrder();
});
{% endif %}
loadStockTable($('#stock-table'), {
url: "{% url 'api-stock-list' %}",
params: {
company: {{ company.id }},
part_detail: true,
supplier_part_detail: true,
location_detail: true,
},
buttons: [
'#stock-options',
],
filterKey: "companystock",
});
$("#stock-export").click(function() {
exportStock({
supplier: {{ company.id }}
});
});
{% if company.is_manufacturer %}
function reloadManufacturerPartTable() {
$('#manufacturer-part-table').bootstrapTable('refresh');
}
$("#manufacturer-part-create").click(function () {
createManufacturerPart({
manufacturer: {{ company.pk }},
onSuccess: function() {
$("#part-table").bootstrapTable("refresh");
}
});
});
loadManufacturerPartTable(
"#manufacturer-part-table",
"{% url 'api-manufacturer-part-list' %}",
{
params: {
part_detail: true,
manufacturer_detail: true,
manufacturer: {{ company.id }},
},
}
);
linkButtonsToSelection($("#manufacturer-part-table"), ['#manufacturer-table-options']);
$("#multi-manufacturer-part-delete").click(function() {
var selections = $("#manufacturer-part-table").bootstrapTable("getSelections");
deleteManufacturerParts(selections, {
onSuccess: function() {
$("#manufacturer-part-table").bootstrapTable("refresh");
}
});
});
$("#multi-manufacturer-part-order").click(function() {
var selections = $("#manufacturer-part-table").bootstrapTable("getSelections");
var parts = [];
selections.forEach(function(item) {
parts.push(item.part);
});
launchModalForm("/order/purchase-order/order-parts/", {
data: {
parts: parts,
},
});
});
{% endif %}
{% if company.is_supplier %}
function reloadSupplierPartTable() {
$('#supplier-part-table').bootstrapTable('refresh');
}
$("#supplier-part-create").click(function () {
createSupplierPart({
supplier: {{ company.pk }},
onSuccess: reloadSupplierPartTable,
});
});
loadSupplierPartTable(
"#supplier-part-table",
"{% url 'api-supplier-part-list' %}",
{
params: {
part_detail: true,
supplier_detail: true,
manufacturer_detail: true,
supplier: {{ company.id }},
},
}
);
linkButtonsToSelection($("#supplier-part-table"), ['#supplier-table-options']);
$("#multi-supplier-part-delete").click(function() {
var selections = $("#supplier-part-table").bootstrapTable("getSelections");
var requests = [];
showQuestionDialog(
'{% trans "Delete Supplier Parts?" %}',
'{% trans "All selected supplier parts will be deleted" %}',
{
accept: function() {
selections.forEach(function(part) {
var url = `/api/company/part/${part.pk}/`;
requests.push(inventreeDelete(url));
});
$.when.apply($, requests).done(function() {
$('#supplier-part-table').bootstrapTable('refresh');
});
}
}
);
});
$("#multi-supplier-part-order").click(function() {
var selections = $("#supplier-part-table").bootstrapTable("getSelections");
var parts = [];
selections.forEach(function(item) {
parts.push(item.part);
});
launchModalForm("/order/purchase-order/order-parts/", {
data: {
parts: parts,
},
});
});
{% endif %}
attachNavCallbacks({
name: 'company',
default: 'company-stock'
});
{% endblock %}

View File

@@ -1,121 +0,0 @@
{% extends "company/company_base.html" %}
{% load static %}
{% load i18n %}
{% load inventree_extras %}
{% block menubar %}
{% include 'company/navbar.html' with tab='manufacturer_parts' %}
{% endblock %}
{% block heading %}
{% trans "Manufacturer Parts" %}
{% endblock %}
{% block details %}
{% if roles.purchase_order.change %}
<div id='button-toolbar'>
<div class='button-toolbar container-fluid'>
<div class='btn-group role='group'>
{% if roles.purchase_order.add %}
<button class="btn btn-success" id='manufacturer-part-create' title='{% trans "Create new manufacturer part" %}'>
<span class='fas fa-plus-circle'></span> {% trans "New Manufacturer Part" %}
</button>
{% endif %}
<div class='btn-group'>
<div class="dropdown" style="float: right;">
<button class="btn btn-primary dropdown-toggle" id='table-options', type="button" data-toggle="dropdown">{% trans "Options" %}
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{% if roles.purchase_order.add %}
<li><a href='#' id='multi-part-order' title='{% trans "Order parts" %}'>{% trans "Order Parts" %}</a></li>
{% endif %}
{% if roles.purchase_order.delete %}
<li><a href='#' id='multi-part-delete' title='{% trans "Delete parts" %}'>{% trans "Delete Parts" %}</a></li>
{% endif %}
</ul>
</div>
</div>
</div>
<div class='filter-list' id='filter-list-supplier-part'>
<!-- Empty div (will be filled out with available BOM filters) -->
</div>
</div>
</div>
{% endif %}
<table class='table table-striped table-condensed' id='part-table' data-toolbar='#button-toolbar'>
</table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
$("#manufacturer-part-create").click(function () {
launchModalForm(
"{% url 'manufacturer-part-create' %}",
{
data: {
manufacturer: {{ company.id }},
},
success: function() {
$("#part-table").bootstrapTable("refresh");
},
secondary: [
{
field: 'part',
label: '{% trans "New Part" %}',
title: '{% trans "Create new Part" %}',
url: "{% url 'part-create' %}"
},
{
field: 'manufacturer',
label: '{% trans "New Manufacturer" %}',
title: '{% trans "Create new Manufacturer" %}',
},
]
});
});
loadManufacturerPartTable(
"#part-table",
"{% url 'api-manufacturer-part-list' %}",
{
params: {
part_detail: true,
manufacturer_detail: true,
company: {{ company.id }},
},
}
);
linkButtonsToSelection($("#manufacturer-table"), ['#table-options']);
$("#multi-part-delete").click(function() {
var selections = $("#part-table").bootstrapTable("getSelections");
deleteManufacturerParts(selections, {
onSuccess: function() {
$("#part-table").bootstrapTable("refresh");
}
});
});
$("#multi-part-order").click(function() {
var selections = $("#part-table").bootstrapTable("getSelections");
var parts = [];
selections.forEach(function(item) {
parts.push(item.part);
});
launchModalForm("/order/purchase-order/order-parts/", {
data: {
parts: parts,
},
});
});
{% endblock %}

View File

@@ -1,49 +0,0 @@
{% extends "company/company_base.html" %}
{% load static %}
{% load i18n %}
{% block menubar %}
{% include "company/navbar.html" with tab='stock' %}
{% endblock %}
{% block heading %}
{% trans "Supplier Stock" %}
{% endblock %}
{% block details %}
{% include "stock_table.html" %}
{% endblock %}
{% block js_ready %}
{{ block.super }}
loadStockTable($('#stock-table'), {
url: "{% url 'api-stock-list' %}",
params: {
company: {{ company.id }},
part_detail: true,
supplier_detail: true,
location_detail: true,
},
buttons: [
'#stock-options',
],
filterKey: "companystock",
});
$("#stock-export").click(function() {
launchModalForm("{% url 'stock-export-options' %}", {
submit_text: '{% trans "Export" %}',
success: function(response) {
var url = "{% url 'stock-export' %}";
url += "?format=" + response.format;
url += "&supplier={{ company.id }}";
location.href = url;
},
});
});
{% endblock %}

View File

@@ -1,127 +0,0 @@
{% extends "company/company_base.html" %}
{% load static %}
{% load i18n %}
{% load inventree_extras %}
{% block menubar %}
{% include 'company/navbar.html' with tab='supplier_parts' %}
{% endblock %}
{% block heading %}
{% trans "Supplier Parts" %}
{% endblock %}
{% block details %}
{% if roles.purchase_order.change %}
<div id='button-toolbar'>
<div class='button-toolbar container-fluid'>
<div class='btn-group' role='group'>
{% if roles.purchase_order.add %}
<button class="btn btn-success" id='supplier-part-create' title='{% trans "Create new supplier part" %}'>
<span class='fas fa-plus-circle'></span> {% trans "New Supplier Part" %}
</button>
{% endif %}
<div class='btn-group'>
<div class="dropdown" style="float: right;">
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{% trans "Options" %}
<span class="caret"></span>
</button>
<ul class="dropdown-menu">
{% if roles.purchase_order.add %}
<li><a href='#' id='multi-part-order' title='{% trans "Order parts" %}'>{% trans "Order Parts" %}</a></li>
{% endif %}
{% if roles.purchase_order.delete %}
<li><a href='#' id='multi-part-delete' title='{% trans "Delete parts" %}'>{% trans "Delete Parts" %}</a></li>
{% endif %}
</ul>
</div>
</div>
</div>
<div class='filter-list' id='filter-list-supplier-part'>
<!-- Empty div (will be filled out with available BOM filters) -->
</div>
</div>
</div>
{% endif %}
<table class='table table-striped table-condensed' id='part-table' data-toolbar='#button-toolbar'>
</table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
$("#supplier-part-create").click(function () {
launchModalForm(
"{% url 'supplier-part-create' %}",
{
data: {
supplier: {{ company.id }},
},
reload: true,
secondary: [
{
field: 'part',
label: '{% trans "New Part" %}',
title: '{% trans "Create new Part" %}',
url: "{% url 'part-create' %}"
},
{
field: 'supplier',
label: "{% trans 'New Supplier' %}",
title: "{% trans 'Create new Supplier' %}",
},
]
});
});
loadSupplierPartTable(
"#part-table",
"{% url 'api-supplier-part-list' %}",
{
params: {
part_detail: true,
supplier_detail: true,
manufacturer_detail: true,
company: {{ company.id }},
},
}
);
$("#multi-part-delete").click(function() {
var selections = $("#part-table").bootstrapTable("getSelections");
var parts = [];
selections.forEach(function(item) {
parts.push(item.pk);
});
var url = "{% url 'supplier-part-delete' %}"
launchModalForm(url, {
data: {
parts: parts,
},
reload: true,
});
});
$("#multi-part-order").click(function() {
var selections = $("#part-table").bootstrapTable("getSelections");
var parts = [];
selections.forEach(function(item) {
parts.push(item.part);
});
launchModalForm("/order/purchase-order/order-parts/", {
data: {
parts: parts,
},
});
});
{% endblock %}

View File

@@ -0,0 +1,321 @@
{% extends "two_column.html" %}
{% load static %}
{% load i18n %}
{% block page_title %}
InvenTree | {% trans "Manufacturer Part" %}
{% endblock %}
{% block menubar %}
{% include "company/manufacturer_part_navbar.html" %}
{% endblock %}
{% block thumbnail %}
<img class='part-thumb'
{% if part.part.image %}
src='{{ part.part.image.url }}'
{% else %}
src="{% static 'img/blank_image.png' %}"
{% endif %}/>
{% endblock %}
{% block page_data %}
<h3>{% trans "Manufacturer Part" %}</h3>
<hr>
<h4>
{{ part.part.full_name }}
{% if user.is_staff and perms.company.change_company %}
<a href="{% url 'admin:company_supplierpart_change' part.pk %}">
<span title='{% trans "Admin view" %}' class='fas fa-user-shield'></span>
</a>
{% endif %}
</h4>
<p>{{ part.manufacturer.name }} - {{ part.MPN }}</p>
{% if roles.purchase_order.change %}
<div class='btn-row'>
<div class='btn-group action-buttons' role='group'>
{% comment "for later" %}
{% if roles.purchase_order.add %}
<button type='button' class='btn btn-default btn-glyph' id='order-part' title='{% trans "Order part" %}'>
<span class='fas fa-shopping-cart'></span>
</button>
{% endif %}
{% endcomment %}
<button type='button' class='btn btn-default btn-glyph' id='edit-part' title='{% trans "Edit manufacturer part" %}'>
<span class='fas fa-edit icon-green'/>
</button>
{% if roles.purchase_order.delete %}
<button type='button' class='btn btn-default btn-glyph' id='delete-part' title='{% trans "Delete manufacturer part" %}'>
<span class='fas fa-trash-alt icon-red'/>
</button>
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}
{% block page_details %}
<h4>{% trans "Manufacturer Part Details" %}</h4>
<table class="table table-striped table-condensed">
<col width='25'>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Internal Part" %}</td>
<td>
{% if part.part %}
<a href="{% url 'part-detail' part.part.id %}?display=part-suppliers">{{ part.part.full_name }}</a>{% include "clip.html"%}
{% endif %}
</td>
</tr>
{% if part.description %}
<tr>
<td></td>
<td>{% trans "Description" %}</td>
<td>{{ part.description }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.link %}
<tr>
<td><span class='fas fa-link'></span></td>
<td>{% trans "External Link" %}</td>
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-industry'></span></td>
<td>{% trans "Manufacturer" %}</td>
<td><a href="{% url 'company-detail' part.manufacturer.id %}">{{ part.manufacturer.name }}</a>{% include "clip.html"%}</td></tr>
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "MPN" %}</td>
<td>{{ part.MPN }}{% include "clip.html"%}</td>
</tr>
</table>
{% endblock %}
{% block page_content %}
<div class='panel panel-default panel-inventree panel-hidden' id='panel-supplier-parts'>
<div class='panel-heading'>
<h4>{% trans "Suppliers" %}</h4>
</div>
<div class='panel-content'>
<div id='supplier-button-toolbar'>
<div class='btn-group'>
<button class="btn btn-success" id='supplier-create'>
<span class='fas fa-plus-circle'></span> {% trans "New Supplier Part" %}
</button>
<div id='opt-dropdown' class="btn-group">
<button id='supplier-part-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{% trans "Options" %} <span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a href='#' id='supplier-part-delete' title='{% trans "Delete supplier parts" %}'>{% trans "Delete" %}</a></li>
</ul>
</div>
</div>
</div>
<table class="table table-striped table-condensed" id='supplier-table' data-toolbar='#supplier-button-toolbar'>
</table>
</div>
</div>
<div class='panel panel-default panel-inventree panel-hidden' id='panel-parameters'>
<div class='panel-heading'>
<h4>{% trans "Parameters" %}</h4>
</div>
<div class='panel-content'>
<div id='parameter-toolbar'>
<div class='btn-group'>
<button class='btn btn-success' id='parameter-create'>
<span class='fas fa-plus-circle'></span> {% trans "New Parameter" %}
</button>
<div id='opt-dropdown' class="btn-group">
<button id='parameter-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{% trans "Options" %} <span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a href='#' id='multi-parameter-delete' title='{% trans "Delete parameters" %}'>{% trans "Delete" %}</a></li>
</ul>
</div>
</div>
</div>
<table class='table table-striped table-condensed' id='parameter-table' data-toolbar='#parameter-toolbar'></table>
</div>
</div>
{% endblock %}
{% block js_ready %}
{{ block.super }}
enableNavbar({
label: 'manufacturer-part',
toggleId: '#manufacturer-part-menu-toggle'
});
function reloadParameters() {
$("#parameter-table").bootstrapTable("refresh");
}
$('#parameter-create').click(function() {
constructForm('{% url "api-manufacturer-part-parameter-list" %}', {
method: 'POST',
fields: {
name: {},
value: {},
units: {},
manufacturer_part: {
value: {{ part.pk }},
hidden: true,
}
},
title: '{% trans "Add Parameter" %}',
onSuccess: reloadParameters
});
});
function reloadSupplierPartTable() {
$('#supplier-table').bootstrapTable('refresh');
}
$('#supplier-create').click(function () {
createSupplierPart({
manufacturer_part: {{ part.pk }},
part: {{ part.part.pk }},
onSuccess: reloadSupplierPartTable,
});
});
$("#supplier-part-delete").click(function() {
var selections = $("#supplier-table").bootstrapTable("getSelections");
var requests = [];
showQuestionDialog(
'{% trans "Delete Supplier Parts?" %}',
'{% trans "All selected supplier parts will be deleted" %}',
{
accept: function() {
selections.forEach(function(part) {
var url = `/api/company/part/${part.pk}/`;
requests.push(inventreeDelete(url));
});
$.when.apply($, requests).done(function() {
reloadSupplierPartTable();
});
}
}
);
});
$("#multi-parameter-delete").click(function() {
var selections = $("#parameter-table").bootstrapTable("getSelections");
var text = `
<div class ='alert alert-block alert-danger'>
<p>{% trans "Selected parameters will be deleted" %}:</p>
<ul>`;
selections.forEach(function(item) {
text += `<li>${item.name} - <em>${item.value}</em></li>`;
});
text += `
</ul>
</div>`;
showQuestionDialog(
'{% trans "Delete Parameters" %}',
text,
{
accept_text: '{% trans "Delete" %}',
accept: function() {
// Delete each parameter via the API
var requests = [];
selections.forEach(function(item) {
var url = `/api/company/part/manufacturer/parameter/${item.pk}/`;
requests.push(inventreeDelete(url));
});
$.when.apply($, requests).done(function() {
$('#parameter-table').bootstrapTable('refresh');
});
}
}
);
});
loadSupplierPartTable(
"#supplier-table",
"{% url 'api-supplier-part-list' %}",
{
params: {
part: {{ part.part.id }},
manufacturer_part: {{ part.id }},
part_detail: false,
supplier_detail: true,
manufacturer_detail: false,
},
}
);
loadManufacturerPartParameterTable(
"#parameter-table",
"{% url 'api-manufacturer-part-parameter-list' %}",
{
params: {
manufacturer_part: {{ part.id }},
}
}
);
linkButtonsToSelection($("#supplier-table"), ['#supplier-part-options']);
linkButtonsToSelection($("#parameter-table"), ['#parameter-options']);
$('#order-part, #order-part2').click(function() {
launchModalForm(
"{% url 'order-parts' %}",
{
data: {
part: {{ part.part.id }},
},
reload: true,
},
);
});
$('#edit-part').click(function () {
editManufacturerPart({{ part.pk }}, {
onSuccess: function() {
location.reload();
}
});
});
$('#delete-part').click(function() {
deleteManufacturerPart({{ part.pk }}, {
onSuccess: function() {
window.location.href = "{% url 'company-detail' part.manufacturer.id %}";
}
});
});
attachNavCallbacks({
name: 'manufacturerpart',
default: 'parameters'
});
{% endblock %}

View File

@@ -1,139 +0,0 @@
{% extends "two_column.html" %}
{% load static %}
{% load i18n %}
{% block page_title %}
InvenTree | {% trans "Manufacturer Part" %}
{% endblock %}
{% block thumbnail %}
<img class='part-thumb'
{% if part.part.image %}
src='{{ part.part.image.url }}'
{% else %}
src="{% static 'img/blank_image.png' %}"
{% endif %}/>
{% endblock %}
{% block page_data %}
<h3>{% trans "Manufacturer Part" %}</h3>
<hr>
<h4>
{{ part.part.full_name }}
{% if user.is_staff and perms.company.change_company %}
<a href="{% url 'admin:company_supplierpart_change' part.pk %}">
<span title='{% trans "Admin view" %}' class='fas fa-user-shield'></span>
</a>
{% endif %}
</h4>
<p>{{ part.manufacturer.name }} - {{ part.MPN }}</p>
{% if roles.purchase_order.change %}
<div class='btn-row'>
<div class='btn-group action-buttons' role='group'>
{% comment "for later" %}
{% if roles.purchase_order.add %}
<button type='button' class='btn btn-default btn-glyph' id='order-part' title='{% trans "Order part" %}'>
<span class='fas fa-shopping-cart'></span>
</button>
{% endif %}
{% endcomment %}
<button type='button' class='btn btn-default btn-glyph' id='edit-part' title='{% trans "Edit manufacturer part" %}'>
<span class='fas fa-edit icon-green'/>
</button>
{% if roles.purchase_order.delete %}
<button type='button' class='btn btn-default btn-glyph' id='delete-part' title='{% trans "Delete manufacturer part" %}'>
<span class='fas fa-trash-alt icon-red'/>
</button>
{% endif %}
</div>
</div>
{% endif %}
{% endblock %}
{% block page_details %}
<h4>{% trans "Manufacturer Part Details" %}</h4>
<table class="table table-striped table-condensed">
<col width='25'>
<tr>
<td><span class='fas fa-shapes'></span></td>
<td>{% trans "Internal Part" %}</td>
<td>
{% if part.part %}
<a href="{% url 'part-manufacturers' part.part.id %}">{{ part.part.full_name }}</a>{% include "clip.html"%}
{% endif %}
</td>
</tr>
{% if part.description %}
<tr>
<td></td>
<td>{% trans "Description" %}</td>
<td>{{ part.description }}{% include "clip.html"%}</td>
</tr>
{% endif %}
{% if part.link %}
<tr>
<td><span class='fas fa-link'></span></td>
<td>{% trans "External Link" %}</td>
<td><a href="{{ part.link }}">{{ part.link }}</a>{% include "clip.html"%}</td>
</tr>
{% endif %}
<tr>
<td><span class='fas fa-industry'></span></td>
<td>{% trans "Manufacturer" %}</td>
<td><a href="{% url 'company-detail-manufacturer-parts' part.manufacturer.id %}">{{ part.manufacturer.name }}</a>{% include "clip.html"%}</td></tr>
<tr>
<td><span class='fas fa-hashtag'></span></td>
<td>{% trans "MPN" %}</td>
<td>{{ part.MPN }}{% include "clip.html"%}</td>
</tr>
</table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
enableNavbar({
label: 'manufacturer-part',
toggleId: '#manufacturer-part-menu-toggle'
})
$('#order-part, #order-part2').click(function() {
launchModalForm(
"{% url 'order-parts' %}",
{
data: {
part: {{ part.part.id }},
},
reload: true,
},
);
});
$('#edit-part').click(function () {
constructForm('{% url "api-manufacturer-part-detail" part.pk %}', {
fields: {
part: {},
manufacturer: {},
MPN: {},
description: {},
link: {},
},
title: '{% trans "Edit Manufacturer Part" %}',
reload: true,
});
});
$('#delete-part').click(function() {
constructForm('{% url "api-manufacturer-part-detail" part.pk %}', {
method: 'DELETE',
title: '{% trans "Delete Manufacturer Part" %}',
redirect: "{% url 'company-detail-manufacturer-parts' part.manufacturer.id %}",
});
});
{% endblock %}

View File

@@ -1,17 +0,0 @@
{% extends "modal_form.html" %}
{% load i18n %}
{% block pre_form_content %}
{{ block.super }}
{% if part %}
<div class='alert alert-block alert-info'>
{% include "hover_image.html" with image=part.image %}
{{ part.full_name}}
<br>
<i>{{ part.description }}</i>
</div>
{% endif %}
{% endblock %}

View File

@@ -1,38 +0,0 @@
{% extends "company/manufacturer_part_base.html" %}
{% load static %}
{% load i18n %}
{% block menubar %}
{% include "company/manufacturer_part_navbar.html" with tab='details' %}
{% endblock %}
{% block heading %}
{% trans "Manufacturer Part Details" %}
{% endblock %}
{% block details %}
<table class="table table-striped table-condensed">
<tr>
<td>{% trans "Internal Part" %}</td>
<td>
{% if part.part %}
<a href="{% url 'part-manufacturers' part.part.id %}">{{ part.part.full_name }}</a>
{% endif %}
</td>
</tr>
<tr><td>{% trans "Manufacturer" %}</td><td><a href="{% url 'company-detail-manufacturer-parts' part.manufacturer.id %}">{{ part.manufacturer.name }}</a></td></tr>
<tr><td>{% trans "MPN" %}</td><td>{{ part.MPN }}</tr></tr>
{% if part.link %}
<tr><td>{% trans "External Link" %}</td><td><a href="{{ part.link }}">{{ part.link }}</a></td></tr>
{% endif %}
</table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
{% endblock %}

View File

@@ -8,8 +8,15 @@
</a>
</li>
<li class='list-group-item {% if tab == "suppliers" %}active{% endif %}' title='{% trans "Supplier Parts" %}'>
<a href='{% url "manufacturer-part-suppliers" part.id %}'>
<li class='list-group-item' title='{% trans "Parameters" %}'>
<a href='#' id='select-parameters' class='nav-toggle'>
<span class='fas fa-th-list sidebar-icon'></span>
{% trans "Parameters" %}
</a>
</li>
<li class='list-group-item' title='{% trans "Supplier Parts" %}'>
<a href='#' id='select-supplier-parts' class='nav-toggle'>
<span class='fas fa-building sidebar-icon'></span>
{% trans "Suppliers" %}
</a>
@@ -17,14 +24,14 @@
{% comment "for later" %}
<li class='list-group-item {% if tab == "stock" %}active{% endif %}' title='{% trans "Manufacturer Part Stock" %}'>
<a href='{% url "manufacturer-part-stock" part.id %}'>
<a href='#'>
<span class='fas fa-boxes sidebar-icon'></span>
{% trans "Stock" %}
</a>
</li>
<li class='list-group-item {% if tab == "orders" %}active{% endif %}' title='{% trans "Manufacturer Part Orders" %}'>
<a href='{% url "manufacturer-part-orders" part.id %}'>
<a href='#'>
<span class='fas fa-shopping-cart sidebar-icon'></span>
{% trans "Orders" %}
</a>

View File

@@ -1,187 +0,0 @@
{% extends "company/manufacturer_part_base.html" %}
{% load static %}
{% load i18n %}
{% block menubar %}
{% include "company/manufacturer_part_navbar.html" with tab='suppliers' %}
{% endblock %}
{% block heading %}
{% trans "Suppliers" %}
{% endblock %}
{% block details %}
<div id='button-toolbar'>
<div class='btn-group'>
<button class="btn btn-success" id='supplier-create'>
<span class='fas fa-plus-circle'></span> {% trans "New Supplier Part" %}
</button>
<div id='opt-dropdown' class="btn-group">
<button id='supplier-part-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{% trans "Options" %}<span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a href='#' id='supplier-part-delete' title='{% trans "Delete supplier parts" %}'>{% trans "Delete" %}</a></li>
</ul>
</div>
</div>
</div>
<table class="table table-striped table-condensed" id='supplier-table' data-toolbar='#button-toolbar'>
</table>
{% endblock %}
{% block post_content_panels %}
<div class='panel panel-default panel-inventree'>
<div class='panel-heading'>
<h4>{% trans "Parameters" %}</h4>
</div>
<div class='panel-content'>
<div id='parameter-toolbar'>
<div class='btn-group'>
<button class='btn btn-success' id='parameter-create'>
<span class='fas fa-plus-circle'></span> {% trans "New Parameter" %}
</button>
<div id='opt-dropdown' class="btn-group">
<button id='parameter-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{% trans "Options" %}<span class="caret"></span></button>
<ul class="dropdown-menu">
<li><a href='#' id='multi-parameter-delete' title='{% trans "Delete parameters" %}'>{% trans "Delete" %}</a></li>
</ul>
</div>
</div>
</div>
<table class='table table-striped table-condensed' id='parameter-table' data-toolbar='#parameter-toolbar'></table>
</div>
</div>
{% endblock %}
{% block js_ready %}
{{ block.super }}
function reloadParameters() {
$("#parameter-table").bootstrapTable("refresh");
}
$('#parameter-create').click(function() {
constructForm('{% url "api-manufacturer-part-parameter-list" %}', {
method: 'POST',
fields: {
name: {},
value: {},
units: {},
manufacturer_part: {
value: {{ part.pk }},
hidden: true,
}
},
title: '{% trans "Add Parameter" %}',
onSuccess: reloadParameters
});
});
$('#supplier-create').click(function () {
launchModalForm(
"{% url 'supplier-part-create' %}",
{
reload: true,
data: {
manufacturer_part: {{ part.id }}
},
secondary: [
{
field: 'supplier',
label: '{% trans "New Supplier" %}',
title: '{% trans "Create new supplier" %}',
},
]
});
});
$("#supplier-part-delete").click(function() {
var selections = $("#supplier-table").bootstrapTable("getSelections");
var parts = [];
selections.forEach(function(item) {
parts.push(item.pk);
});
launchModalForm("{% url 'supplier-part-delete' %}", {
data: {
parts: parts,
},
reload: true,
});
});
$("#multi-parameter-delete").click(function() {
var selections = $("#parameter-table").bootstrapTable("getSelections");
var text = `
<div class ='alert alert-block alert-danger'>
<p>{% trans "Selected parameters will be deleted" %}:</p>
<ul>`;
selections.forEach(function(item) {
text += `<li>${item.name} - <i>${item.value}</i></li>`;
});
text += `
</ul>
</div>`;
showQuestionDialog(
'{% trans "Delete Parameters" %}',
text,
{
accept_text: '{% trans "Delete" %}',
accept: function() {
// Delete each parameter via the API
var requests = [];
selections.forEach(function(item) {
var url = `/api/company/part/manufacturer/parameter/${item.pk}/`;
requests.push(inventreeDelete(url));
});
$.when.apply($, requests).then(function() {
$('#parameter-table').bootstrapTable('refresh');
});
}
}
);
});
loadSupplierPartTable(
"#supplier-table",
"{% url 'api-supplier-part-list' %}",
{
params: {
part: {{ part.part.id }},
manufacturer_part: {{ part.id }},
part_detail: false,
supplier_detail: true,
manufacturer_detail: false,
},
}
);
loadManufacturerPartParameterTable(
"#parameter-table",
"{% url 'api-manufacturer-part-parameter-list' %}",
{
params: {
manufacturer_part: {{ part.id }},
}
}
);
linkButtonsToSelection($("#supplier-table"), ['#supplier-part-options'])
linkButtonsToSelection($("#parameter-table"), ['#parameter-options'])
{% endblock %}

View File

@@ -9,25 +9,18 @@
</a>
</li>
<li class='list-group-item {% if tab == "details" %}active{% endif %}' title='{% trans "Company Details" %}'>
<a href='{% url "company-detail" company.id %}'>
<span class='fas fa-info-circle sidebar-icon'></span>
{% trans "Details" %}
</a>
</li>
{% if company.is_manufacturer %}
<li class='list-group-item {% if tab == "manufacturer_parts" %}active{% endif %}' title='{% trans "Manufactured Parts" %}'>
<a href='{% url "company-detail-manufacturer-parts" company.id %}'>
<li class='list-group-item' title='{% trans "Manufactured Parts" %}'>
<a href='#' id='select-manufacturer-parts' class='nav-toggle'>
<span class='fas fa-industry sidebar-icon'></span>
{% trans "Manufactured Parts" %}
</a>
</li>
{% endif %}
{% if company.is_supplier or company.is_manufacturer %}
<li class='list-group-item {% if tab == "supplier_parts" %}active{% endif %}' title='{% trans "Supplied Parts" %}'>
<a href='{% url "company-detail-supplier-parts" company.id %}'>
{% if company.is_supplier %}
<li class='list-group-item' title='{% trans "Supplied Parts" %}'>
<a href='#' id='select-supplier-parts' class='nav-toggle'>
<span class='fas fa-building sidebar-icon'></span>
{% trans "Supplied Parts" %}
</a>
@@ -35,8 +28,8 @@
{% endif %}
{% if company.is_manufacturer or company.is_supplier %}
<li class='list-group-item {% if tab == "stock" %}active{% endif %}' title='{% trans "Stock Items" %}'>
<a href='{% url "company-detail-stock" company.id %}'>
<li class='list-group-item' title='{% trans "Stock Items" %}'>
<a href='#' id='select-company-stock' class='nav-toggle'>
<span class='fas fa-boxes sidebar-icon'></span>
{% trans "Stock" %}
</a>
@@ -44,8 +37,8 @@
{% endif %}
{% if company.is_supplier %}
<li class='list-group-item {% if tab == "po" %}active{% endif %}' title='{% trans "Sales Orders" %}'>
<a href='{% url "company-detail-purchase-orders" company.id %}'>
<li class='list-group-item' title='{% trans "Purchase Orders" %}'>
<a href='#' id='select-purchase-orders' class='nav-toggle'>
<span class='fas fa-shopping-cart sidebar-icon'></span>
{% trans "Purchase Orders" %}
</a>
@@ -53,22 +46,22 @@
{% endif %}
{% if company.is_customer %}
<li class='list-group-item {% if tab == "so" %}active{% endif %}' title='{% trans "Sales Orders" %}'>
<a href='{% url "company-detail-sales-orders" company.id %}'>
<li class='list-group-item' title='{% trans "Sales Orders" %}'>
<a href='#' id='select-sales-orders' class='nav-toggle'>
<span class='fas fa-truck sidebar-icon'></span>
{% trans "Sales Orders" %}
</a>
</li>
<li class='list-group-item {% if tab == "assigned" %}active{% endif %}' title='{% trans "Assigned Stock" %}'>
<a href='{% url "company-detail-assigned-stock" company.id %}'>
<li class='list-group-item' title='{% trans "Assigned Stock" %}'>
<a href='#' id='select-assigned-stock' class='nav-toggle'>
<span class='fas fa-sign-out-alt sidebar-icon'></span>
{% trans "Assigned Stock" %}
</a>
</li>
{% endif %}
<li class='list-group-item {% if tab == "notes" %}active{% endif %}' titl='{% trans "Notes" %}'>
<a href='{% url "company-notes" company.id %}'>
<li class='list-group-item' title='{% trans "Notes" %}'>
<a href='#' id='select-company-notes' class='nav-toggle'>
<span class='fas fa-clipboard sidebar-icon'></span>
{% trans "Notes" %}
</a>

View File

@@ -1,47 +0,0 @@
{% extends "company/company_base.html" %}
{% load static %}
{% load i18n %}
{% load markdownify %}
{% block menubar %}
{% include 'company/navbar.html' with tab='notes' %}
{% endblock %}
{% block heading %}
{% trans "Company Notes" %}
{% if not editing %}
<button title='{% trans "Edit notes" %}' class='btn btn-default' id='edit-notes'><span class='fas fa-edit'></span></button>
{% endif %}
{% endblock %}
{% block details %}
{% if editing %}
<form method='POST'>
{% csrf_token %}
{{ form }}
<hr>
<button type="submit" class='btn btn-default'>{% trans "Save" %}</button>
</form>
{{ form.media }}
{% else %}
{{ company.notes | markdownify }}
{% endif %}
{% endblock %}
{% block js_ready %}
{{ block.super }}
{% if editing %}
{% else %}
$("#edit-notes").click(function() {
location.href = "{% url 'company-notes' company.id %}?edit=1";
});
{% endif %}
{% endblock %}

View File

@@ -1,56 +0,0 @@
{% extends "company/company_base.html" %}
{% load static %}
{% load i18n %}
{% block menubar %}
{% include 'company/navbar.html' with tab='po' %}
{% endblock %}
{% block heading %}
{% trans "Purchase Orders" %}
{% endblock %}
{% block details %}
{% if roles.purchase_order.add %}
<div id='button-bar'>
<div class='button-toolbar container-fluid' style='float: right;'>
<button class='btn btn-primary' type='button' id='company-order2' title='{% trans "Create new purchase order" %}'>
<span class='fas fa-plus-circle'></span> {% trans "New Purchase Order" %}</button>
<div class='filter-list' id='filter-list-purchaseorder'>
<!-- Empty div -->
</div>
</div>
</div>
{% endif %}
<table class='table table-striped table-condensed po-table' id='purchase-order-table' data-toolbar='#button-bar'>
</table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
loadPurchaseOrderTable("#purchase-order-table", {
url: "{% url 'api-po-list' %}",
params: {
supplier: {{ company.id }},
}
});
function newOrder() {
createPurchaseOrder({
supplier: {{ company.pk }},
});
}
$("#company-order").click(function() {
newOrder();
});
$("#company-order2").click(function() {
newOrder();
});
{% endblock %}

View File

@@ -1,51 +0,0 @@
{% extends "company/company_base.html" %}
{% load static %}
{% load i18n %}
{% block menubar %}
{% include 'company/navbar.html' with tab='so' %}
{% endblock %}
{% block heading %}
{% trans "Sales Orders" %}
{% endblock %}
{% block details %}
{% if roles.sales_order.add %}
<div id='button-bar'>
<div class='button-toolbar container-fluid' style='float: right;'>
<button class='btn btn-primary' type='button' id='new-sales-order' title='{% trans "Create new sales order" %}'>
<div class='fas fa-plus-circle'></div> {% trans "New Sales Order" %}
</button>
<div class='filter-list' id='filter-list-salesorder'>
<!-- Empty div -->
</div>
</div>
</div>
{% endif %}
<table class='table table-striped table-condensed po-table' id='sales-order-table' data-toolbar='#button-bar'>
</table>
{% endblock %}
{% block js_ready %}
{{ block.super }}
loadSalesOrderTable("#sales-order-table", {
url: "{% url 'api-so-list' %}",
params: {
customer: {{ company.id }},
}
});
$("#new-sales-order").click(function() {
createSalesOrder({
customer: {{ company.pk }},
});
});
{% endblock %}

Some files were not shown because too many files have changed in this diff Show More