diff --git a/.github/ISSUE_TEMPLATE/app_issue.md b/.github/ISSUE_TEMPLATE/app_issue.md
new file mode 100644
index 0000000000..e71861394c
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/app_issue.md
@@ -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*
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 4af3fc5386..0677e61de4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,22 +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.
+*Note: A github action checks for unstaged migration files and will reject the PR if it finds any!*
-## Testing
+## Unit Testing
-Any new code should be covered by unit tests - a submitted PR may not be accepted if the code coverage is decreased.
+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.
+
+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):
+
+- Checking Python and Javascript code against standard style guides
+- Running unit test suite
+- Automated building and pushing of docker images
+- Generating translation files
+
+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 %}
+
+{% trans "This string will be translated" %} - this string will not!
+```
\ No newline at end of file
diff --git a/InvenTree/part/test_api.py b/InvenTree/part/test_api.py
index ebef21b84b..660b573e33 100644
--- a/InvenTree/part/test_api.py
+++ b/InvenTree/part/test_api.py
@@ -588,6 +588,27 @@ class PartAPITest(InvenTreeAPITestCase):
self.assertEqual(new_part.supplier_parts.count(), 1)
self.assertEqual(new_part.manufacturer_parts.count(), 1)
+ def test_strange_chars(self):
+ """
+ Test that non-standard ASCII chars are accepted
+ """
+
+ url = reverse('api-part-list')
+
+ name = "Kaltgerätestecker"
+ description = "Gerät"
+
+ data = {
+ "name": name,
+ "description": description,
+ "category": 2
+ }
+
+ response = self.post(url, data, expected_code=201)
+
+ self.assertEqual(response.data['name'], name)
+ self.assertEqual(response.data['description'], description)
+
class PartDetailTests(InvenTreeAPITestCase):
"""
diff --git a/InvenTree/templates/js/translated/modals.js b/InvenTree/templates/js/translated/modals.js
index 5195c67b46..96e41fd6ec 100644
--- a/InvenTree/templates/js/translated/modals.js
+++ b/InvenTree/templates/js/translated/modals.js
@@ -793,14 +793,25 @@ function attachSecondaries(modal, secondaries) {
function insertActionButton(modal, options) {
/* Insert a custom submission button */
- var html = `
-
-
- `;
+ var element = $(modal).find('#modal-footer-buttons');
- $(modal).find('#modal-footer-buttons').append(html);
+ // check if button already present
+ var already_present = false;
+ for (var child=element[0].firstElementChild; child; child=child.nextElementSibling) {
+ if (item.firstElementChild.name == options.name) {
+ already_present = true;
+ }
+ }
+
+ if (already_present == false) {
+ var html = `
+
+
+ `;
+ element.append(html);
+ }
}
function attachButtons(modal, buttons) {