mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
017bf187f9
@ -83,6 +83,7 @@ INSTALLED_APPS = [
|
||||
'django.contrib.staticfiles',
|
||||
|
||||
# InvenTree apps
|
||||
'common.apps.CommonConfig',
|
||||
'part.apps.PartConfig',
|
||||
'stock.apps.StockConfig',
|
||||
'company.apps.CompanyConfig',
|
||||
@ -231,16 +232,20 @@ USE_TZ = True
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/1.10/howto/static-files/
|
||||
|
||||
# Web URL endpoint for served static files
|
||||
STATIC_URL = '/static/'
|
||||
|
||||
# The filesystem location for served static files
|
||||
STATIC_ROOT = CONFIG.get('static_root', os.path.join(BASE_DIR, 'static'))
|
||||
|
||||
STATICFILES_DIRS = [
|
||||
os.path.join(BASE_DIR, 'InvenTree', 'static'),
|
||||
]
|
||||
|
||||
# Web URL endpoint for served media files
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
# The filesystem location for served static files
|
||||
MEDIA_ROOT = CONFIG.get('media_root', os.path.join(BASE_DIR, 'media'))
|
||||
|
||||
# crispy forms use the bootstrap templates
|
||||
|
@ -4,7 +4,7 @@ Provides information on the current InvenTree version
|
||||
|
||||
import subprocess
|
||||
|
||||
INVENTREE_SW_VERSION = "0.0.3"
|
||||
INVENTREE_SW_VERSION = "0.0.4"
|
||||
|
||||
|
||||
def inventreeVersion():
|
||||
|
0
InvenTree/common/__init__.py
Normal file
0
InvenTree/common/__init__.py
Normal file
10
InvenTree/common/admin.py
Normal file
10
InvenTree/common/admin.py
Normal file
@ -0,0 +1,10 @@
|
||||
from django.contrib import admin
|
||||
|
||||
from .models import Currency
|
||||
|
||||
|
||||
class CurrencyAdmin(admin.ModelAdmin):
|
||||
list_display = ('symbol', 'suffix', 'description', 'value', 'base')
|
||||
|
||||
|
||||
admin.site.register(Currency, CurrencyAdmin)
|
5
InvenTree/common/apps.py
Normal file
5
InvenTree/common/apps.py
Normal file
@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class CommonConfig(AppConfig):
|
||||
name = 'common'
|
16
InvenTree/common/fixtures/currency.yaml
Normal file
16
InvenTree/common/fixtures/currency.yaml
Normal file
@ -0,0 +1,16 @@
|
||||
# Test fixtures for Currency objects
|
||||
|
||||
- model: common.currency
|
||||
fields:
|
||||
symbol: '$'
|
||||
suffix: 'AUD'
|
||||
description: 'Australian Dollars'
|
||||
base: True
|
||||
|
||||
- model: common.currency
|
||||
fields:
|
||||
symbol: '$'
|
||||
suffix: 'USD'
|
||||
description: 'US Dollars'
|
||||
base: False
|
||||
value: 1.4
|
26
InvenTree/common/migrations/0001_initial.py
Normal file
26
InvenTree/common/migrations/0001_initial.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Generated by Django 2.2.4 on 2019-09-02 23:02
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Currency',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('symbol', models.CharField(help_text='Currency Symbol e.g. $', max_length=10)),
|
||||
('suffix', models.CharField(help_text='Currency Suffix e.g. AUD', max_length=10, unique=True)),
|
||||
('description', models.CharField(help_text='Currency Description', max_length=100)),
|
||||
('value', models.DecimalField(decimal_places=5, help_text='Currency Value', max_digits=10, validators=[django.core.validators.MinValueValidator(1e-05), django.core.validators.MaxValueValidator(100000)])),
|
||||
('base', models.BooleanField(default=False, help_text='Use this currency as the base currency')),
|
||||
],
|
||||
),
|
||||
]
|
17
InvenTree/common/migrations/0002_auto_20190902_2304.py
Normal file
17
InvenTree/common/migrations/0002_auto_20190902_2304.py
Normal file
@ -0,0 +1,17 @@
|
||||
# Generated by Django 2.2.4 on 2019-09-02 23:04
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('common', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='currency',
|
||||
options={'verbose_name_plural': 'Currencies'},
|
||||
),
|
||||
]
|
19
InvenTree/common/migrations/0003_auto_20190902_2310.py
Normal file
19
InvenTree/common/migrations/0003_auto_20190902_2310.py
Normal file
@ -0,0 +1,19 @@
|
||||
# Generated by Django 2.2.4 on 2019-09-02 23:10
|
||||
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('common', '0002_auto_20190902_2304'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='currency',
|
||||
name='value',
|
||||
field=models.DecimalField(decimal_places=5, default=1.0, help_text='Currency Value', max_digits=10, validators=[django.core.validators.MinValueValidator(1e-05), django.core.validators.MaxValueValidator(100000)]),
|
||||
),
|
||||
]
|
0
InvenTree/common/migrations/__init__.py
Normal file
0
InvenTree/common/migrations/__init__.py
Normal file
79
InvenTree/common/models.py
Normal file
79
InvenTree/common/models.py
Normal file
@ -0,0 +1,79 @@
|
||||
"""
|
||||
Common database model definitions.
|
||||
These models are 'generic' and do not fit a particular business logic object.
|
||||
"""
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
|
||||
|
||||
class Currency(models.Model):
|
||||
"""
|
||||
A Currency object represents a particular unit of currency.
|
||||
Each Currency has a scaling factor which relates it to the base currency.
|
||||
There must be one (and only one) currency which is selected as the base currency,
|
||||
and each other currency is calculated relative to it.
|
||||
|
||||
Attributes:
|
||||
symbol: Currency symbol e.g. $
|
||||
suffix: Currency suffix e.g. AUD
|
||||
description: Long-form description e.g. "Australian Dollars"
|
||||
value: The value of this currency compared to the base currency.
|
||||
base: True if this currency is the base currency
|
||||
|
||||
"""
|
||||
|
||||
symbol = models.CharField(max_length=10, blank=False, unique=False, help_text=_('Currency Symbol e.g. $'))
|
||||
|
||||
suffix = models.CharField(max_length=10, blank=False, unique=True, help_text=_('Currency Suffix e.g. AUD'))
|
||||
|
||||
description = models.CharField(max_length=100, blank=False, help_text=_('Currency Description'))
|
||||
|
||||
value = models.DecimalField(default=1.0, max_digits=10, decimal_places=5, validators=[MinValueValidator(0.00001), MaxValueValidator(100000)], help_text=_('Currency Value'))
|
||||
|
||||
base = models.BooleanField(default=False, help_text=_('Use this currency as the base currency'))
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = 'Currencies'
|
||||
|
||||
def __str__(self):
|
||||
""" Format string for currency representation """
|
||||
s = "{sym} {suf} - {desc}".format(
|
||||
sym=self.symbol,
|
||||
suf=self.suffix,
|
||||
desc=self.description
|
||||
)
|
||||
|
||||
if self.base:
|
||||
s += " (Base)"
|
||||
|
||||
else:
|
||||
s += " = {v}".format(v=self.value)
|
||||
|
||||
return s
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
""" Validate the model before saving
|
||||
|
||||
- Ensure that there is only one base currency!
|
||||
"""
|
||||
|
||||
# If this currency is set as the base currency, ensure no others are
|
||||
if self.base:
|
||||
for cur in Currency.objects.filter(base=True).exclude(pk=self.pk):
|
||||
cur.base = False
|
||||
cur.save()
|
||||
|
||||
# If there are no currencies set as the base currency, set this as base
|
||||
if not Currency.objects.filter(base=True).exists():
|
||||
self.base = True
|
||||
|
||||
# If this is the base currency, ensure value is set to unity
|
||||
if self.base:
|
||||
self.value = 1.0
|
||||
|
||||
super().save(*args, **kwargs)
|
19
InvenTree/common/tests.py
Normal file
19
InvenTree/common/tests.py
Normal file
@ -0,0 +1,19 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from .models import Currency
|
||||
|
||||
|
||||
class CurrencyTest(TestCase):
|
||||
""" Tests for Currency model """
|
||||
|
||||
fixtures = [
|
||||
'currency',
|
||||
]
|
||||
|
||||
def test_currency(self):
|
||||
# Simple test for now (improve this later!)
|
||||
|
||||
self.assertEqual(Currency.objects.count(), 2)
|
1
InvenTree/common/views.py
Normal file
1
InvenTree/common/views.py
Normal file
@ -0,0 +1 @@
|
||||
# Create your views here.
|
@ -70,5 +70,6 @@ class EditPriceBreakForm(HelperForm):
|
||||
fields = [
|
||||
'part',
|
||||
'quantity',
|
||||
'cost'
|
||||
'cost',
|
||||
'currency',
|
||||
]
|
||||
|
@ -0,0 +1,20 @@
|
||||
# Generated by Django 2.2.4 on 2019-09-02 23:34
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('common', '0003_auto_20190902_2310'),
|
||||
('company', '0005_auto_20190525_2356'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='supplierpricebreak',
|
||||
name='currency',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='common.Currency'),
|
||||
),
|
||||
]
|
@ -8,6 +8,7 @@ from __future__ import unicode_literals
|
||||
import os
|
||||
|
||||
import math
|
||||
from decimal import Decimal
|
||||
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
@ -19,6 +20,7 @@ from django.conf import settings
|
||||
from django.contrib.staticfiles.templatetags.staticfiles import static
|
||||
|
||||
from InvenTree.status_codes import OrderStatus
|
||||
from common.models import Currency
|
||||
|
||||
|
||||
def rename_company_image(instance, filename):
|
||||
@ -310,7 +312,8 @@ class SupplierPart(models.Model):
|
||||
# If this price-break quantity is the largest so far, use it!
|
||||
if pb.quantity > pb_quantity:
|
||||
pb_quantity = pb.quantity
|
||||
pb_cost = pb.cost
|
||||
# Convert everything to base currency
|
||||
pb_cost = pb.converted_cost
|
||||
|
||||
if pb_found:
|
||||
cost = pb_cost * quantity
|
||||
@ -369,6 +372,7 @@ class SupplierPriceBreak(models.Model):
|
||||
part: Link to a SupplierPart object that this price break applies to
|
||||
quantity: Quantity required for price break
|
||||
cost: Cost at specified quantity
|
||||
currency: Reference to the currency of this pricebreak (leave empty for base currency)
|
||||
"""
|
||||
|
||||
part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks')
|
||||
@ -377,6 +381,19 @@ class SupplierPriceBreak(models.Model):
|
||||
|
||||
cost = models.DecimalField(max_digits=10, decimal_places=5, validators=[MinValueValidator(0)])
|
||||
|
||||
currency = models.ForeignKey(Currency, blank=True, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
@property
|
||||
def converted_cost(self):
|
||||
""" Return the cost of this price break, converted to the base currency """
|
||||
|
||||
scaler = Decimal(1.0)
|
||||
|
||||
if self.currency:
|
||||
scaler = self.currency.value
|
||||
|
||||
return self.cost * scaler
|
||||
|
||||
class Meta:
|
||||
unique_together = ("part", "quantity")
|
||||
|
||||
|
@ -32,6 +32,8 @@ class CompanySerializer(InvenTreeModelSerializer):
|
||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||
part_count = serializers.CharField(read_only=True)
|
||||
|
||||
image = serializers.CharField(source='get_image_url', read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = Company
|
||||
fields = [
|
||||
|
@ -88,7 +88,10 @@ InvenTree | {{ company.name }} - Parts
|
||||
{% for pb in part.price_breaks.all %}
|
||||
<tr>
|
||||
<td>{{ pb.quantity }}</td>
|
||||
<td>{{ pb.cost }}
|
||||
<td>
|
||||
{% if pb.currency %}{{ pb.currency.symbol }}{% endif %}
|
||||
{{ pb.cost }}
|
||||
{% if pb.currency %}{{ pb.currency.suffix }}{% endif %}
|
||||
<div class='btn-group' style='float: right;'>
|
||||
<button title='Edit Price Break' class='btn btn-primary pb-edit-button btn-sm' type='button' url="{% url 'price-break-edit' pb.id %}"><span class='glyphicon glyphicon-small glyphicon-edit'></span></button>
|
||||
<button title='Delete Price Break' class='btn btn-danger pb-delete-button btn-sm' type='button' url="{% url 'price-break-delete' pb.id %}"><span class='glyphicon glyphicon-small glyphicon-trash'></span></button>
|
||||
|
@ -28,6 +28,10 @@
|
||||
<tr>
|
||||
<td>View Code on GitHub</td><td><a href="{% inventree_github %}">{% inventree_github %}</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><a href='https://github.com/inventree/InvenTree/issues'><button class='btn btn-default'>Submit Bug Report</button></a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
5
Makefile
5
Makefile
@ -9,6 +9,7 @@ clean:
|
||||
|
||||
# Perform database migrations (after schema changes are made)
|
||||
migrate:
|
||||
python3 InvenTree/manage.py makemigrations common
|
||||
python3 InvenTree/manage.py makemigrations company
|
||||
python3 InvenTree/manage.py makemigrations part
|
||||
python3 InvenTree/manage.py makemigrations stock
|
||||
@ -40,12 +41,12 @@ style:
|
||||
# Run unit tests
|
||||
test:
|
||||
python3 InvenTree/manage.py check
|
||||
python3 InvenTree/manage.py test build company part stock order
|
||||
python3 InvenTree/manage.py test build common company order part stock
|
||||
|
||||
# Run code coverage
|
||||
coverage:
|
||||
python3 InvenTree/manage.py check
|
||||
coverage run InvenTree/manage.py test build company part stock order InvenTree
|
||||
coverage run InvenTree/manage.py test build common company order part stock InvenTree
|
||||
coverage html
|
||||
|
||||
# Install packages required to generate code docs
|
||||
|
@ -9,9 +9,9 @@ InvenTree Configuration
|
||||
|
||||
Admin users will need to adjust the InvenTree installation to meet the particular needs of their setup. For example, pointing to the correct database backend, or specifying a list of allowed hosts.
|
||||
|
||||
The Django configuration parameters are found in the normal place (``settings.py``). However the settings presented in this file should not be adjusted as they will alter the core behaviour of the InvenTree application.
|
||||
The Django configuration parameters are found in the normal place (*settings.py*). However the settings presented in this file should not be adjusted as they will alter the core behaviour of the InvenTree application.
|
||||
|
||||
To support install specific settings, a simple configuration file ``config.yaml`` is provided. This configuration file is loaded by ``settings.py`` at runtime. Settings specific to a given install should be adjusted in ``config.yaml``.
|
||||
To support install specific settings, a simple configuration file ``config.yaml`` is provided. This configuration file is loaded by **settings.py** at runtime. Settings specific to a given install should be adjusted in ``config.yaml``.
|
||||
|
||||
The default configuration file launches a *DEBUG* configuration with a simple SQLITE database backend. This default configuration file is shown below:
|
||||
|
||||
|
@ -31,7 +31,9 @@ Directories for storing *media* files and *static* files should be specified in
|
||||
Collect Static Files
|
||||
--------------------
|
||||
|
||||
The required static files must be collected into the specified ``STATIC_ROOT`` directory. Run ``python3 manage.py collectstatic``
|
||||
The required static files must be collected into the specified ``STATIC_ROOT`` directory:
|
||||
|
||||
``python3 InvenTree/manage.py collectstatic``
|
||||
|
||||
Configure Gunicorn
|
||||
------------------
|
||||
|
@ -10,11 +10,11 @@ InvenTree Source Documentation
|
||||
Getting Started<start>
|
||||
Configuration<config>
|
||||
Deployment<deploy>
|
||||
Migrate Data<migrate>
|
||||
Backup and Restore<backup>
|
||||
Modal Forms<forms>
|
||||
Tables<tables>
|
||||
REST API<rest>
|
||||
Backup and Restore<backup>
|
||||
Migrate Data<migrate>
|
||||
InvenTree Modules <modules>
|
||||
Module Reference<reference>
|
||||
|
||||
|
@ -12,7 +12,7 @@ In the case that data needs to be migrated from one database installation to ano
|
||||
Export Data
|
||||
-----------
|
||||
|
||||
``python3 manage.py dumpdata --exclude contenttypes --exclude auth.permission --indent 2 > data.json``
|
||||
``python3 InvenTree/manage.py dumpdata --exclude contenttypes --exclude auth.permission --indent 2 > data.json``
|
||||
|
||||
This will export all data (including user information) to a json data file.
|
||||
|
||||
@ -21,13 +21,16 @@ Initialize Database
|
||||
|
||||
Configure the new database using the normal processes (see `Getting Started <start.html>`_):
|
||||
|
||||
``python3 manage.py makemigrations``
|
||||
``python3 InvenTree/manage.py makemigrations``
|
||||
|
||||
``python3 manage.py migrate --run-syncdb``
|
||||
``python3 InvenTree/manage.py migrate --run-syncdb``
|
||||
|
||||
Import Data
|
||||
-----------
|
||||
|
||||
The new database should now be correctly initialized with the correct table structures requried to import the data.
|
||||
|
||||
``python3 manage.py loaddata data.json``
|
||||
``python3 InvenTree/manage.py loaddata data.json``
|
||||
|
||||
.. important::
|
||||
If the character encoding of the data file does not exactly match the target database, the import operation may not succeed. In this case, some manual editing of the data file may be required.
|
@ -14,10 +14,10 @@ A makefile in the root directory provides shortcuts for the installation process
|
||||
Requirements
|
||||
------------
|
||||
|
||||
To install InvenTree you will need the following:
|
||||
To install InvenTree you will need the following system components installed:
|
||||
|
||||
* python3
|
||||
* pip3
|
||||
* python3-pip
|
||||
* make
|
||||
|
||||
Installation
|
||||
@ -35,17 +35,17 @@ To setup the InvenTree environment, *cd into the inventree directory* and run th
|
||||
|
||||
which installs all required Python packages using pip package manager. It also creates a (default) database configuration file which needs to be edited to meet user needs before proceeding (see next step below).
|
||||
|
||||
Additionally, this step creates a *SECREY_KEY* file which is used for the django authentication framework.
|
||||
Additionally, this step creates a *SECRET_KEY* file which is used for the django authentication framework.
|
||||
|
||||
.. important::
|
||||
The *SECREY_KEY* file should never be shared or made public.
|
||||
The *SECRET_KEY* file should never be shared or made public.
|
||||
|
||||
Database Configuration
|
||||
-----------------------
|
||||
|
||||
Once the required packages are installed, the database configuration must be adjusted to suit your particular needs. InvenTree provides a simple default setup which should work *out of the box* for testing and debug purposes.
|
||||
|
||||
As part of the previous *install* step, a configuration file (*config.yaml*) is created. The configuration file provides administrators control over various setup options without digging into the Django ``settings.py`` script. The default setup uses a local sqlite database with *DEBUG* mode enabled.
|
||||
As part of the previous *install* step, a configuration file (``config.yaml``) is created. The configuration file provides administrators control over various setup options without digging into the Django *settings.py* script. The default setup uses a local sqlite database with *DEBUG* mode enabled.
|
||||
|
||||
For further information on installation configuration, refer to the `Configuration <config.html>`_ section.
|
||||
|
||||
@ -82,9 +82,10 @@ Development and Testing
|
||||
|
||||
Other shorthand functions are provided for the development and testing process:
|
||||
|
||||
* ``make requirements`` - Install all required underlying packages using PIP
|
||||
* ``make setup`` - Perform one-time setup functions
|
||||
* ``make install`` - Install all required underlying packages using PIP
|
||||
* ``make superuser`` - Create a superuser account
|
||||
* ``make migrate`` - Perform database migrations
|
||||
* ``make mysql`` - Install packages required for MySQL database backend
|
||||
* ``make backup`` - Backup database tables and media files
|
||||
* ``make test`` - Run all unit tests
|
||||
* ``make coverage`` - Run all unit tests and generate code coverage report
|
||||
|
Loading…
Reference in New Issue
Block a user