Finally got migrations working together

This commit is contained in:
Oliver 2018-04-22 21:54:12 +10:00
parent cef3c664f9
commit 4bedf0ed4c
86 changed files with 356 additions and 2279 deletions

View File

@ -4,6 +4,7 @@ from django.contrib import admin
from part.urls import part_api_urls, part_cat_api_urls
from part.urls import bom_api_urls
from part.urls import part_urls
from part.urls import supplier_part_urls
from stock.urls import stock_api_urls, stock_api_loc_urls
from stock.urls import stock_urls
@ -69,6 +70,7 @@ urlpatterns = [
url(r'^part/', include(part_urls)),
url(r'^stock/', include(stock_urls)),
url(r'^company/', include(company_urls)),
url(r'^supplier-part/', include(supplier_part_urls)),
url(r'^build/', include(build_urls)),
url(r'^admin/', admin.site.urls),

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-16 14:03
# Generated by Django 1.11.12 on 2018-04-22 11:53
from __future__ import unicode_literals
import django.core.validators
@ -12,7 +12,7 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('part', '0019_auto_20180416_1249'),
('part', '0001_initial'),
]
operations = [
@ -20,21 +20,14 @@ class Migration(migrations.Migration):
name='Build',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.PositiveIntegerField(choices=[(b'20', b'Allocated'), (b'30', b'Cancelled'), (b'40', b'Complete'), (b'10', b'Pending')], default=10)),
],
),
migrations.CreateModel(
name='BuildOutput',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('part_name', models.CharField(max_length=255)),
('batch', models.CharField(blank=True, help_text='Batch code for this build output', max_length=100, null=True)),
('status', models.PositiveIntegerField(choices=[(40, 'Complete'), (10, 'Pending'), (20, 'Holding'), (30, 'Cancelled')], default=10, validators=[django.core.validators.MinValueValidator(0)])),
('creation_date', models.DateField(auto_now=True)),
('completion_date', models.DateField(blank=True, null=True)),
('title', models.CharField(help_text='Brief description of the build', max_length=100)),
('quantity', models.PositiveIntegerField(default=1, help_text='Number of parts to build', validators=[django.core.validators.MinValueValidator(1)])),
('build', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='outputs', to='build.Build')),
('notes', models.TextField(blank=True)),
('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part')),
],
),
migrations.AlterUniqueTogether(
name='buildoutput',
unique_together=set([('part', 'build')]),
),
]

View File

@ -1,36 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-16 14:23
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('build', '0001_initial'),
]
operations = [
migrations.AlterUniqueTogether(
name='buildoutput',
unique_together=set([]),
),
migrations.RemoveField(
model_name='buildoutput',
name='build',
),
migrations.RemoveField(
model_name='buildoutput',
name='part',
),
migrations.AddField(
model_name='build',
name='quantity',
field=models.PositiveIntegerField(default=1, help_text='Number of parts to build', validators=[django.core.validators.MinValueValidator(1)]),
),
migrations.DeleteModel(
name='BuildOutput',
),
]

View File

@ -1,23 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-16 14:28
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0019_auto_20180416_1249'),
('build', '0002_auto_20180416_1423'),
]
operations = [
migrations.AddField(
model_name='build',
name='part',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part'),
preserve_default=False,
),
]

View File

@ -1,48 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 06:57
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0019_auto_20180416_1249'),
('build', '0003_build_part'),
]
operations = [
migrations.CreateModel(
name='BuildOutput',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1, help_text='Number of parts to build', validators=[django.core.validators.MinValueValidator(1)])),
],
),
migrations.RemoveField(
model_name='build',
name='part',
),
migrations.RemoveField(
model_name='build',
name='quantity',
),
migrations.AlterField(
model_name='build',
name='status',
field=models.PositiveIntegerField(choices=[(40, 'Cancelled'), (10, 'Pending'), (20, 'Allocated'), (50, 'Complete'), (30, 'Holding')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.AddField(
model_name='buildoutput',
name='build',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='outputs', to='build.Build'),
),
migrations.AddField(
model_name='buildoutput',
name='part',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part'),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-17 11:27
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('build', '0003_build_part'),
]
operations = [
migrations.AlterField(
model_name='build',
name='status',
field=models.PositiveIntegerField(choices=[('20', 'Allocated'), ('30', 'Cancelled'), ('40', 'Complete'), ('10', 'Pending')], default=10),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 08:29
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('build', '0004_auto_20180417_0657'),
]
operations = [
migrations.AddField(
model_name='buildoutput',
name='batch',
field=models.CharField(blank=True, help_text='Batch code for this build output', max_length=100),
),
]

View File

@ -1,66 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 09:33
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0022_auto_20180417_0819'),
('build', '0005_buildoutput_batch'),
]
operations = [
migrations.RemoveField(
model_name='buildoutput',
name='build',
),
migrations.RemoveField(
model_name='buildoutput',
name='part',
),
migrations.AddField(
model_name='build',
name='batch',
field=models.CharField(blank=True, help_text='Batch code for this build output', max_length=100, null=True),
),
migrations.AddField(
model_name='build',
name='completion_date',
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name='build',
name='creation_date',
field=models.DateField(auto_now=True),
),
migrations.AddField(
model_name='build',
name='notes',
field=models.CharField(blank=True, max_length=500),
),
migrations.AddField(
model_name='build',
name='part',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='builds', to='part.Part'),
preserve_default=False,
),
migrations.AddField(
model_name='build',
name='quantity',
field=models.PositiveIntegerField(default=1, help_text='Number of parts to build', validators=[django.core.validators.MinValueValidator(1)]),
),
migrations.AddField(
model_name='build',
name='title',
field=models.CharField(default='Build title', help_text='Brief description of the build', max_length=100),
preserve_default=False,
),
migrations.DeleteModel(
name='BuildOutput',
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 10:25
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('build', '0006_auto_20180417_0933'),
]
operations = [
migrations.AlterField(
model_name='build',
name='status',
field=models.PositiveIntegerField(choices=[(40, 'Complete'), (10, 'Pending'), (20, 'Holding'), (30, 'Cancelled')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
),
]

View File

@ -1,16 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-17 13:07
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('build', '0004_auto_20180417_2127'),
('build', '0007_auto_20180417_1025'),
]
operations = [
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 13:16
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('build', '0008_merge_20180417_2307'),
]
operations = [
migrations.AlterField(
model_name='build',
name='status',
field=models.PositiveIntegerField(choices=[(40, 'Complete'), (10, 'Pending'), (20, 'Holding'), (30, 'Cancelled')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-17 14:28
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('build', '0009_auto_20180417_1316'),
]
operations = [
migrations.AlterField(
model_name='build',
name='status',
field=models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Holding'), (30, 'Cancelled'), (40, 'Complete')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 15:14
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('build', '0010_auto_20180418_0028'),
]
operations = [
migrations.AlterField(
model_name='build',
name='status',
field=models.PositiveIntegerField(choices=[(40, 'Complete'), (10, 'Pending'), (20, 'Holding'), (30, 'Cancelled')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 15:16
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('build', '0011_auto_20180417_1514'),
]
operations = [
migrations.AlterField(
model_name='build',
name='notes',
field=models.TextField(blank=True),
),
]

View File

@ -6,7 +6,7 @@ from django.utils.translation import ugettext as _
from django.db import models
from django.core.validators import MinValueValidator
from part.models import Part
#from part.models import Part
class Build(models.Model):
@ -49,7 +49,7 @@ class Build(models.Model):
# A reference to the part being built
# Only 'buildable' parts can be selected
part = models.ForeignKey(Part, on_delete=models.CASCADE,
part = models.ForeignKey('part.Part', on_delete=models.CASCADE,
related_name='builds',
limit_choices_to={'buildable': True},
)

View File

@ -1,21 +1,16 @@
from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from .models import Company, SupplierPart
from .models import SupplierOrder
from .models import Company
#from .models import SupplierOrder
class CompanyAdmin(ImportExportModelAdmin):
list_display = ('name', 'website', 'contact')
class SupplierPartAdmin(ImportExportModelAdmin):
list_display = ('part', 'supplier', 'SKU')
class SupplierOrderAdmin(admin.ModelAdmin):
list_display = ('internal_ref', 'supplier', 'issued_date', 'delivery_date', 'status')
#class SupplierOrderAdmin(admin.ModelAdmin):
# list_display = ('internal_ref', 'supplier', 'issued_date', 'delivery_date', 'status')
admin.site.register(Company, CompanyAdmin)
admin.site.register(SupplierPart, SupplierPartAdmin)
admin.site.register(SupplierOrder, SupplierOrderAdmin)
#admin.site.register(SupplierOrder, SupplierOrderAdmin)

View File

@ -2,10 +2,10 @@ from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from .models import Company, SupplierPart
from .models import SupplierOrder
from .models import Company
"""
class EditSupplierOrderForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
@ -26,7 +26,7 @@ class EditSupplierOrderForm(forms.ModelForm):
'notes',
'issued_date',
]
"""
class EditCompanyForm(forms.ModelForm):
@ -50,29 +50,6 @@ class EditCompanyForm(forms.ModelForm):
'phone',
'email',
'contact',
'image',
'notes'
]
class EditSupplierPartForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(EditSupplierPartForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'id-edit-part-form'
self.helper.form_class = 'blueForms'
self.helper.form_method = 'post'
self.helper.add_input(Submit('submit', 'Submit'))
class Meta:
model = SupplierPart
fields = [
'supplier',
'SKU',
'part',
'description',
'URL',
'manufacturer',
'MPN',
]

View File

@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-12 05:02
# Generated by Django 1.11.12 on 2018-04-22 11:53
from __future__ import unicode_literals
import django.core.validators
import company.models
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
@ -12,92 +11,22 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('part', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='Customer',
name='Company',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
('website', models.URLField(blank=True)),
('address', models.CharField(blank=True, max_length=200)),
('name', models.CharField(help_text='Company naem', max_length=100, unique=True)),
('description', models.CharField(max_length=500)),
('website', models.URLField(blank=True, help_text='Company website URL')),
('address', models.CharField(blank=True, help_text='Company address', max_length=200)),
('phone', models.CharField(blank=True, max_length=50)),
('email', models.EmailField(blank=True, max_length=254)),
('contact', models.CharField(blank=True, max_length=100)),
('notes', models.CharField(blank=True, max_length=500)),
('image', models.ImageField(blank=True, max_length=255, null=True, upload_to=company.models.rename_company_image)),
('notes', models.TextField(blank=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Manufacturer',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
('website', models.URLField(blank=True)),
('address', models.CharField(blank=True, max_length=200)),
('phone', models.CharField(blank=True, max_length=50)),
('email', models.EmailField(blank=True, max_length=254)),
('contact', models.CharField(blank=True, max_length=100)),
('notes', models.CharField(blank=True, max_length=500)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Supplier',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100, unique=True)),
('website', models.URLField(blank=True)),
('address', models.CharField(blank=True, max_length=200)),
('phone', models.CharField(blank=True, max_length=50)),
('email', models.EmailField(blank=True, max_length=254)),
('contact', models.CharField(blank=True, max_length=100)),
('notes', models.CharField(blank=True, max_length=500)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='SupplierPart',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('SKU', models.CharField(max_length=100)),
('MPN', models.CharField(blank=True, max_length=100)),
('URL', models.URLField(blank=True)),
('description', models.CharField(blank=True, max_length=250)),
('single_price', models.DecimalField(decimal_places=3, default=0, max_digits=10)),
('base_cost', models.DecimalField(decimal_places=3, default=0, max_digits=10)),
('packaging', models.CharField(blank=True, max_length=50)),
('multiple', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)])),
('minimum', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)])),
('lead_time', models.DurationField(blank=True, null=True)),
('manufacturer', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='supplier.Manufacturer')),
('part', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='part.Part')),
('supplier', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='supplier.Supplier')),
],
),
migrations.CreateModel(
name='SupplierPriceBreak',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(0)])),
('cost', models.DecimalField(decimal_places=3, max_digits=10)),
('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='price_breaks', to='supplier.SupplierPart')),
],
),
migrations.AlterUniqueTogether(
name='supplierpricebreak',
unique_together=set([('part', 'quantity')]),
),
migrations.AlterUniqueTogether(
name='supplierpart',
unique_together=set([('part', 'supplier', 'SKU')]),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-12 06:22
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('supplier', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='supplierpart',
name='part',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part'),
),
]

View File

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-14 05:40
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supplier', '0002_auto_20180412_0622'),
]
operations = [
migrations.AddField(
model_name='customer',
name='description',
field=models.CharField(blank=True, max_length=500),
),
migrations.AddField(
model_name='manufacturer',
name='description',
field=models.CharField(blank=True, max_length=500),
),
migrations.AddField(
model_name='supplier',
name='description',
field=models.CharField(blank=True, max_length=500),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-14 06:24
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('supplier', '0003_auto_20180414_0540'),
]
operations = [
migrations.AlterField(
model_name='supplierpart',
name='supplier',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='parts', to='supplier.Supplier'),
),
]

View File

@ -1,30 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-15 02:55
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supplier', '0004_auto_20180414_0624'),
]
operations = [
migrations.AlterField(
model_name='customer',
name='description',
field=models.CharField(max_length=500),
),
migrations.AlterField(
model_name='manufacturer',
name='description',
field=models.CharField(max_length=500),
),
migrations.AlterField(
model_name='supplier',
name='description',
field=models.CharField(max_length=500),
),
]

View File

@ -1,26 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-15 10:11
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('supplier', '0005_auto_20180415_0255'),
]
operations = [
migrations.AlterField(
model_name='supplierpart',
name='manufacturer',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='supplier.Manufacturer'),
),
migrations.AlterField(
model_name='supplierpart',
name='part',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='supplier_parts', to='part.Part'),
),
]

View File

@ -1,36 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-16 12:53
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('supplier', '0006_auto_20180415_1011'),
]
operations = [
migrations.AlterField(
model_name='supplierpart',
name='MPN',
field=models.CharField(blank=True, help_text='Manufacturer part number', max_length=100),
),
migrations.AlterField(
model_name='supplierpart',
name='SKU',
field=models.CharField(help_text='Supplier stock keeping unit', max_length=100),
),
migrations.AlterField(
model_name='supplierpart',
name='manufacturer',
field=models.ForeignKey(blank=True, help_text='Manufacturer', null=True, on_delete=django.db.models.deletion.SET_NULL, to='supplier.Manufacturer'),
),
migrations.AlterField(
model_name='supplierpart',
name='part',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part'),
),
]

View File

@ -1,18 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 13:37
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('supplier', '0007_auto_20180416_1253'),
]
operations = [
migrations.DeleteModel(
name='Customer',
),
]

View File

@ -1,42 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 14:11
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('supplier', '0008_delete_customer'),
]
operations = [
migrations.CreateModel(
name='SupplierOrder',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('internal_ref', models.CharField(max_length=25, unique=True)),
('created_date', models.DateField(auto_now_add=True)),
('issued_date', models.DateField(blank=True, help_text='Date the purchase order was issued')),
('notes', models.TextField(blank=True, help_text='Order notes')),
('supplier', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='supplier.Supplier')),
],
),
migrations.CreateModel(
name='SupplierOrderLineItem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('line_number', models.PositiveIntegerField(default=1)),
('quantity', models.PositiveIntegerField(default=1)),
('notes', models.TextField(blank=True)),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='supplier.SupplierOrder')),
('part', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='supplier.SupplierPart')),
],
),
migrations.AlterUniqueTogether(
name='supplierorderlineitem',
unique_together=set([('order', 'line_number'), ('order', 'part')]),
),
]

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 15:16
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supplier', '0008_delete_customer'),
]
operations = [
migrations.AlterField(
model_name='manufacturer',
name='notes',
field=models.TextField(blank=True),
),
migrations.AlterField(
model_name='supplier',
name='notes',
field=models.TextField(blank=True),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 14:20
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supplier', '0009_auto_20180417_1411'),
]
operations = [
migrations.AlterField(
model_name='supplierorder',
name='issued_date',
field=models.DateField(blank=True, help_text='Date the purchase order was issued', null=True),
),
]

View File

@ -1,61 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 14:36
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('supplier', '0010_auto_20180417_1420'),
]
operations = [
migrations.AlterField(
model_name='manufacturer',
name='address',
field=models.CharField(blank=True, help_text='Company address', max_length=200),
),
migrations.AlterField(
model_name='manufacturer',
name='name',
field=models.CharField(help_text='Company naem', max_length=100, unique=True),
),
migrations.AlterField(
model_name='manufacturer',
name='notes',
field=models.TextField(blank=True),
),
migrations.AlterField(
model_name='manufacturer',
name='website',
field=models.URLField(blank=True, help_text='Company website URL'),
),
migrations.AlterField(
model_name='supplier',
name='address',
field=models.CharField(blank=True, help_text='Company address', max_length=200),
),
migrations.AlterField(
model_name='supplier',
name='name',
field=models.CharField(help_text='Company naem', max_length=100, unique=True),
),
migrations.AlterField(
model_name='supplier',
name='notes',
field=models.TextField(blank=True),
),
migrations.AlterField(
model_name='supplier',
name='website',
field=models.URLField(blank=True, help_text='Company website URL'),
),
migrations.AlterField(
model_name='supplierorder',
name='supplier',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='orders', to='supplier.Supplier'),
),
]

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 14:47
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supplier', '0011_auto_20180417_1436'),
]
operations = [
migrations.AddField(
model_name='supplierorder',
name='delivery_date',
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name='supplierorder',
name='status',
field=models.PositiveIntegerField(choices=[(40, 'Cancelled'), (10, 'Pending'), (20, 'Placed'), (50, 'Lost'), (30, 'Received')], default=10),
),
]

View File

@ -1,16 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 15:17
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('supplier', '0009_auto_20180417_1516'),
('supplier', '0012_auto_20180417_1447'),
]
operations = [
]

View File

@ -1,31 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 15:20
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0022_auto_20180417_0819'),
('supplier', '0013_merge_20180417_1517'),
]
operations = [
migrations.RenameField(
model_name='supplierorderlineitem',
old_name='part',
new_name='supplier_part',
),
migrations.AddField(
model_name='supplierorderlineitem',
name='internal_part',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='part.Part'),
),
migrations.AlterUniqueTogether(
name='supplierorderlineitem',
unique_together=set([('order', 'line_number'), ('order', 'internal_part'), ('order', 'supplier_part')]),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 15:22
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('supplier', '0014_auto_20180417_1520'),
]
operations = [
migrations.AddField(
model_name='supplierorderlineitem',
name='received',
field=models.BooleanField(default=False),
),
]

View File

@ -1,16 +1,31 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import os
from django.utils.translation import ugettext as _
from django.db import models
from django.core.validators import MinValueValidator
from part.models import Part
def rename_company_image(instance, filename):
base = 'company_images'
if filename.count('.') > 0:
ext = filename.split('.')[-1]
else:
ext = ''
fn = 'company_{pk}_img'.format(pk=instance.pk)
if ext:
fn += '.' + ext
return os.path.join(base,fn)
class Company(models.Model):
""" Abstract model representing an external company
"""
name = models.CharField(max_length=100, unique=True,
help_text='Company naem')
@ -30,6 +45,8 @@ class Company(models.Model):
contact = models.CharField(max_length=100,
blank=True)
image = models.ImageField(upload_to=rename_company_image, max_length=255, null=True, blank=True)
notes = models.TextField(blank=True)
def __str__(self):
@ -54,149 +71,3 @@ class Company(models.Model):
def has_orders(self):
return self.order_count > 0
class SupplierPart(models.Model):
""" Represents a unique part as provided by a Supplier
Each SupplierPart is identified by a MPN (Manufacturer Part Number)
Each SupplierPart is also linked to a Part object
- A Part may be available from multiple suppliers
"""
def get_absolute_url(self):
return "/supplier/part/{id}/".format(id=self.id)
class Meta:
unique_together = ('part', 'supplier', 'SKU')
# Link to an actual part
# The part will have a field 'supplier_parts' which links to the supplier part options
part = models.ForeignKey(Part, on_delete=models.CASCADE,
related_name='supplier_parts')
supplier = models.ForeignKey(Company, on_delete=models.CASCADE,
related_name='parts')
SKU = models.CharField(max_length=100, help_text='Supplier stock keeping unit')
manufacturer = models.CharField(max_length=100, blank=True, help_text='Manufacturer')
MPN = models.CharField(max_length=100, blank=True, help_text='Manufacturer part number')
URL = models.URLField(blank=True)
description = models.CharField(max_length=250, blank=True)
# Default price for a single unit
single_price = models.DecimalField(max_digits=10, decimal_places=3, default=0)
# Base charge added to order independent of quantity e.g. "Reeling Fee"
base_cost = models.DecimalField(max_digits=10, decimal_places=3, default=0)
# packaging that the part is supplied in, e.g. "Reel"
packaging = models.CharField(max_length=50, blank=True)
# multiple that the part is provided in
multiple = models.PositiveIntegerField(default=1, validators=[MinValueValidator(0)])
# Mimumum number required to order
minimum = models.PositiveIntegerField(default=1, validators=[MinValueValidator(0)])
# lead time for parts that cannot be delivered immediately
lead_time = models.DurationField(blank=True, null=True)
def __str__(self):
return "{sku} - {supplier}".format(
sku=self.SKU,
supplier=self.supplier.name)
class SupplierPriceBreak(models.Model):
""" Represents a quantity price break for a SupplierPart
- Suppliers can offer discounts at larger quantities
- SupplierPart(s) may have zero-or-more associated SupplierPriceBreak(s)
"""
part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='price_breaks')
quantity = models.PositiveIntegerField(validators=[MinValueValidator(0)])
cost = models.DecimalField(max_digits=10, decimal_places=3)
class Meta:
unique_together = ("part", "quantity")
def __str__(self):
return "{mpn} - {cost}{currency} @ {quan}".format(
mpn=self.part.MPN,
cost=self.cost,
currency=self.currency if self.currency else '',
quan=self.quantity)
class SupplierOrder(models.Model):
"""
An order of parts from a supplier, made up of multiple line items
"""
def get_absolute_url(self):
return "/supplier/order/{id}/".format(id=self.id)
# Interal reference for this order
internal_ref = models.CharField(max_length=25, unique=True)
supplier = models.ForeignKey(Company, on_delete=models.CASCADE,
related_name='orders')
created_date = models.DateField(auto_now_add=True, editable=False)
issued_date = models.DateField(blank=True, null=True, help_text="Date the purchase order was issued")
notes = models.TextField(blank=True, help_text="Order notes")
def __str__(self):
return "PO {ref} ({status})".format(ref=self.internal_ref,
status=self.get_status_display)
PENDING = 10 # Order is pending (not yet placed)
PLACED = 20 # Order has been placed
RECEIVED = 30 # Order has been received
CANCELLED = 40 # Order was cancelled
LOST = 50 # Order was lost
ORDER_STATUS_CODES = {PENDING: _("Pending"),
PLACED: _("Placed"),
CANCELLED: _("Cancelled"),
RECEIVED: _("Received"),
LOST: _("Lost")
}
status = models.PositiveIntegerField(default=PENDING,
choices=ORDER_STATUS_CODES.items())
delivery_date = models.DateField(blank=True, null=True)
class SupplierOrderLineItem(models.Model):
"""
A line item in a supplier order, corresponding to some quantity of part
"""
class Meta:
unique_together = [
('order', 'line_number'),
('order', 'supplier_part'),
('order', 'internal_part'),
]
order = models.ForeignKey(SupplierOrder, on_delete=models.CASCADE)
line_number = models.PositiveIntegerField(default=1)
internal_part = models.ForeignKey(Part, null=True, blank=True, on_delete=models.SET_NULL)
supplier_part = models.ForeignKey(SupplierPart, null=True, blank=True, on_delete=models.SET_NULL)
quantity = models.PositiveIntegerField(default=1)
notes = models.TextField(blank=True)
received = models.BooleanField(default=False)

View File

@ -2,26 +2,26 @@
{% block content %}
<h3>Suppliers</h3>
<h3>Companies</h3>
<ul class='list-group'>
{% for supplier in suppliers %}
{% for company in companies %}
<li class='list-group-item'>
<b><a href="{% url 'supplier-detail' supplier.id %}">{{ supplier.name }}</a></b>
<b><a href="{% url 'company-detail' company.id %}">{{ supplier.name }}</a></b>
<br>
{{ supplier.description }}
{% if supplier.website %}
<a href="{{ supplier.website }}">- {{ supplier.website }}</a>
{{ company.description }}
{% if company.website %}
<a href="{{ company.website }}">- {{ company.website }}</a>
{% endif %}
<span class="badge">
{{ supplier.parts.all|length }}
{{ company.parts.all|length }}
</span>
</li>
{% endfor %}
</ul>
<div class='container-fluid'>
<a href="{% url 'supplier-create' %}">
<button class="btn btn-success">New Supplier</button>
<a href="{% url 'company-create' %}">
<button class="btn btn-success">New Company</button>
</a>
</div>

View File

@ -3,94 +3,20 @@ from django.views.generic.base import RedirectView
from . import views
"""
cust_urls = [
# Customer detail
url(r'^(?P<pk>[0-9]+)/?$', api.CustomerDetail.as_view(), name='customer-detail'),
# List customers
url(r'^\?.*/?$', api.CustomerList.as_view()),
url(r'^$', api.CustomerList.as_view())
]
manu_urls = [
# Manufacturer detail
url(r'^(?P<pk>[0-9]+)/?$', api.ManufacturerDetail.as_view(), name='manufacturer-detail'),
# List manufacturers
url(r'^\?.*/?$', api.ManufacturerList.as_view()),
url(r'^$', api.ManufacturerList.as_view())
]
supplier_api_part_urls = [
url(r'^(?P<pk>[0-9]+)/?$', api.SupplierPartDetail.as_view(), name='supplierpart-detail'),
url(r'^\?.*/?$', api.SupplierPartList.as_view()),
url(r'^$', api.SupplierPartList.as_view())
]
price_break_urls = [
url(r'^(?P<pk>[0-9]+)/?$', api.SupplierPriceBreakDetail.as_view(), name='supplierpricebreak-detail'),
url(r'^\?.*/?$', api.SupplierPriceBreakList.as_view()),
url(r'^$', api.SupplierPriceBreakList.as_view())
]
supplier_api_urls = [
# Display details of a supplier
url(r'^(?P<pk>[0-9]+)/$', api.SupplierDetail.as_view(), name='supplier-detail'),
# List suppliers
url(r'^\?.*/?$', api.SupplierList.as_view()),
url(r'^$', api.SupplierList.as_view())
]
"""
company_detail_urls = [
url(r'edit/?', views.CompanyEdit.as_view(), name='company-edit'),
url(r'delete/?', views.CompanyDelete.as_view(), name='company-delete'),
url(r'orders/?', views.CompanyDetail.as_view(template_name='supplier/orders.html'), name='company-detail-orders'),
# url(r'orders/?', views.CompanyDetail.as_view(template_name='company/orders.html'), name='company-detail-orders'),
url(r'^.*$', views.CompanyDetail.as_view(), name='company-detail'),
]
supplier_part_detail_urls = [
url(r'edit/?', views.SupplierPartEdit.as_view(), name='supplier-part-edit'),
url(r'delete/?', views.SupplierPartDelete.as_view(), name='supplier-part-delete'),
url('^.*$', views.SupplierPartDetail.as_view(), name='supplier-part-detail'),
]
supplier_part_urls = [
url(r'^new/?', views.SupplierPartCreate.as_view(), name='supplier-part-create'),
url(r'^(?P<pk>\d+)/', include(supplier_part_detail_urls)),
]
"""
supplier_order_detail_urls = [
url('^.*$', views.SupplierOrderDetail.as_view(), name='supplier-order-detail'),
]
supplier_order_urls = [
url(r'^new/?', views.SupplierOrderCreate.as_view(), name='supplier-order-create'),
url(r'^(?P<pk>\d+)/', include(supplier_order_detail_urls)),
]
"""
company_urls = [
url(r'supplier_part/', include(supplier_part_urls)),
#url(r'order/', include(supplier_order_urls)),
#url(r'new/?', views.SupplierCreate.as_view(), name='supplier-create'),
url(r'new/?', views.CompanyCreate.as_view(), name='company-create'),
url(r'^(?P<pk>\d+)/', include(company_detail_urls)),

View File

@ -6,13 +6,12 @@ from django.views.generic.edit import UpdateView, DeleteView, CreateView
from part.models import Part
from .models import Company
from .models import SupplierPart
from .models import SupplierOrder
#from .models import SupplierOrder
from .forms import EditCompanyForm
from .forms import EditSupplierPartForm
from .forms import EditSupplierOrderForm
#from .forms import EditSupplierOrderForm
"""
class SupplierOrderDetail(DetailView):
context_object_name = 'order'
model = SupplierOrder
@ -35,6 +34,7 @@ class SupplierOrderCreate(CreateView):
initials['supplier'] = get_object_or_404(Supplier, pk=s_id)
return initials
"""
class CompanyIndex(ListView):
@ -44,7 +44,7 @@ class CompanyIndex(ListView):
paginate_by = 50
def get_queryset(self):
return Supplier.objects.order_by('name')
return Company.objects.order_by('name')
class CompanyDetail(DetailView):
@ -58,11 +58,12 @@ class CompanyEdit(UpdateView):
model = Company
form_class = EditCompanyForm
template_name = 'company/edit.html'
context_object_name = 'supplier'
context_object_name = 'company'
class CompanyCreate(CreateView):
model = Company
context_object_name = 'company'
form_class = EditCompanyForm
template_name = "company/create.html"
@ -79,51 +80,3 @@ class CompanyDelete(DeleteView):
return HttpResponseRedirect(self.get_object().get_absolute_url())
class SupplierPartDetail(DetailView):
model = SupplierPart
template_name = 'company/partdetail.html'
context_object_name = 'part'
queryset = SupplierPart.objects.all()
class SupplierPartEdit(UpdateView):
model = SupplierPart
template_name = 'company/partedit.html'
context_object_name = 'part'
form_class = EditSupplierPartForm
class SupplierPartCreate(CreateView):
model = SupplierPart
form_class = EditSupplierPartForm
template_name = 'company/partcreate.html'
context_object_name = 'part'
def get_initial(self):
initials = super(SupplierPartCreate, self).get_initial().copy()
supplier_id = self.request.GET.get('supplier', None)
part_id = self.request.GET.get('part', None)
if supplier_id:
initials['supplier'] = get_object_or_404(Supplier, pk=supplier_id)
# TODO
# self.fields['supplier'].disabled = True
if part_id:
initials['part'] = get_object_or_404(Part, pk=part_id)
# TODO
# self.fields['part'].disabled = True
return initials
class SupplierPartDelete(DeleteView):
model = SupplierPart
success_url = '/supplier/'
template_name = 'company/partdelete.html'
def post(self, request, *args, **kwargs):
if 'confirm' in request.POST:
return super(SupplierPartDelete, self).post(request, *args, **kwargs)
else:
return HttpResponseRedirect(self.get_object().get_absolute_url())

View File

@ -2,6 +2,7 @@ from django.contrib import admin
from import_export.admin import ImportExportModelAdmin
from .models import PartCategory, Part
from .models import SupplierPart
from .models import BomItem
from .models import PartAttachment
@ -20,8 +21,11 @@ class BomItemAdmin(ImportExportModelAdmin):
list_display = ('part', 'sub_part', 'quantity')
class PartAttachmentAdmin(admin.ModelAdmin):
list_display = ('part', 'attachment')
#class PartAttachmentAdmin(admin.ModelAdmin):
# list_display = ('part', 'attachment')
class SupplierPartAdmin(ImportExportModelAdmin):
list_display = ('part', 'supplier', 'SKU')
"""
@ -36,8 +40,4 @@ class ParameterAdmin(admin.ModelAdmin):
admin.site.register(Part, PartAdmin)
admin.site.register(PartCategory, PartCategoryAdmin)
admin.site.register(BomItem, BomItemAdmin)
admin.site.register(PartAttachment, PartAttachmentAdmin)
# admin.site.register(PartParameter, ParameterAdmin)
# admin.site.register(PartParameterTemplate, ParameterTemplateAdmin)
# admin.site.register(CategoryParameterLink)
admin.site.register(SupplierPart, SupplierPartAdmin)

View File

@ -3,6 +3,7 @@ from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from .models import Part, PartCategory, BomItem
from .models import SupplierPart
class EditPartForm(forms.ModelForm):
@ -73,3 +74,27 @@ class EditBomItemForm(forms.ModelForm):
'sub_part',
'quantity'
]
class EditSupplierPartForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(EditSupplierPartForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_id = 'id-edit-part-form'
self.helper.form_class = 'blueForms'
self.helper.form_method = 'post'
self.helper.add_input(Submit('submit', 'Submit'))
class Meta:
model = SupplierPart
fields = [
'supplier',
'SKU',
'part',
'description',
'URL',
'manufacturer',
'MPN',
]

View File

@ -1,10 +1,11 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-12 05:02
# Generated by Django 1.11.12 on 2018-04-22 11:53
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
import part.models
class Migration(migrations.Migration):
@ -12,42 +13,57 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('company', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='CategoryParameterLink',
name='BomItem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)])),
],
options={
'verbose_name': 'Category Parameter',
'verbose_name_plural': 'Category Parameters',
'verbose_name': 'BOM Item',
},
),
migrations.CreateModel(
name='Part',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.CharField(blank=True, max_length=250)),
('IPN', models.CharField(blank=True, max_length=100)),
('minimum_stock', models.PositiveIntegerField(default=0, validators=[django.core.validators.MinValueValidator(0)])),
('name', models.CharField(help_text='Part name (must be unique)', max_length=100, unique=True)),
('description', models.CharField(help_text='Part description', max_length=250)),
('IPN', models.CharField(blank=True, help_text='Internal Part Number', max_length=100)),
('URL', models.URLField(blank=True, help_text='Link to extenal URL')),
('image', models.ImageField(blank=True, max_length=255, null=True, upload_to=part.models.rename_part_image)),
('minimum_stock', models.PositiveIntegerField(default=0, help_text='Minimum allowed stock level', validators=[django.core.validators.MinValueValidator(0)])),
('units', models.CharField(blank=True, default='pcs', max_length=20)),
('trackable', models.BooleanField(default=False)),
('buildable', models.BooleanField(default=False, help_text='Can this part be built from other parts?')),
('trackable', models.BooleanField(default=False, help_text='Does this part have tracking for unique items?')),
('purchaseable', models.BooleanField(default=True, help_text='Can this part be purchased from external suppliers?')),
('salable', models.BooleanField(default=False, help_text='Can this part be sold to customers?')),
('notes', models.TextField(blank=True)),
],
options={
'verbose_name': 'Part',
'verbose_name_plural': 'Parts',
},
),
migrations.CreateModel(
name='PartAttachment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('attachment', models.FileField(blank=True, null=True, upload_to=part.models.attach_file)),
('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='part.Part')),
],
),
migrations.CreateModel(
name='PartCategory',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.CharField(blank=True, max_length=250)),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='part.PartCategory')),
('name', models.CharField(max_length=100, unique=True)),
('description', models.CharField(max_length=250)),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='part.PartCategory')),
],
options={
'verbose_name': 'Part Category',
@ -55,62 +71,63 @@ class Migration(migrations.Migration):
},
),
migrations.CreateModel(
name='PartParameter',
name='SupplierPart',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('value', models.CharField(blank=True, max_length=50)),
('min_value', models.CharField(blank=True, max_length=50)),
('max_value', models.CharField(blank=True, max_length=50)),
('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='parameters', to='part.Part')),
('SKU', models.CharField(help_text='Supplier stock keeping unit', max_length=100)),
('manufacturer', models.CharField(blank=True, help_text='Manufacturer', max_length=100)),
('MPN', models.CharField(blank=True, help_text='Manufacturer part number', max_length=100)),
('URL', models.URLField(blank=True)),
('description', models.CharField(blank=True, max_length=250)),
('single_price', models.DecimalField(decimal_places=3, default=0, max_digits=10)),
('base_cost', models.DecimalField(decimal_places=3, default=0, max_digits=10)),
('packaging', models.CharField(blank=True, max_length=50)),
('multiple', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)])),
('minimum', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)])),
('lead_time', models.DurationField(blank=True, null=True)),
('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='supplier_parts', to='part.Part')),
('supplier', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='parts', to='company.Company')),
],
options={
'verbose_name': 'Part Parameter',
'verbose_name_plural': 'Part Parameters',
},
),
migrations.CreateModel(
name='PartParameterTemplate',
name='SupplierPriceBreak',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=20, unique=True)),
('units', models.CharField(blank=True, max_length=10)),
('format', models.PositiveIntegerField(choices=[(10, 'Numeric'), (20, 'Text'), (30, 'Bool')], default=10, validators=[django.core.validators.MinValueValidator(0)])),
('quantity', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(0)])),
('cost', models.DecimalField(decimal_places=3, max_digits=10)),
('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='price_breaks', to='part.SupplierPart')),
],
options={
'verbose_name': 'Parameter Template',
'verbose_name_plural': 'Parameter Templates',
},
),
migrations.AddField(
model_name='partparameter',
name='template',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='part.PartParameterTemplate'),
),
migrations.AddField(
model_name='part',
name='category',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='part.PartCategory'),
field=models.ForeignKey(blank=True, help_text='Part category', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='parts', to='part.PartCategory'),
),
migrations.AddField(
model_name='categoryparameterlink',
name='category',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='part.PartCategory'),
model_name='part',
name='default_supplier',
field=models.ForeignKey(blank=True, help_text='Default supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='part.SupplierPart'),
),
migrations.AddField(
model_name='categoryparameterlink',
name='template',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='part.PartParameterTemplate'),
),
migrations.AlterUniqueTogether(
name='partparameter',
unique_together=set([('part', 'template')]),
),
migrations.AlterUniqueTogether(
model_name='bomitem',
name='part',
unique_together=set([('name', 'category'), ('IPN', 'category')]),
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'),
),
migrations.AddField(
model_name='bomitem',
name='sub_part',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'),
),
migrations.AlterUniqueTogether(
name='categoryparameterlink',
unique_together=set([('category', 'template')]),
name='supplierpricebreak',
unique_together=set([('part', 'quantity')]),
),
migrations.AlterUniqueTogether(
name='supplierpart',
unique_together=set([('part', 'supplier', 'SKU')]),
),
migrations.AlterUniqueTogether(
name='bomitem',
unique_together=set([('part', 'sub_part')]),
),
]

View File

@ -1,48 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-12 06:00
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('part', '0001_initial'),
]
operations = [
migrations.AlterUniqueTogether(
name='categoryparameterlink',
unique_together=set([]),
),
migrations.RemoveField(
model_name='categoryparameterlink',
name='category',
),
migrations.RemoveField(
model_name='categoryparameterlink',
name='template',
),
migrations.AlterUniqueTogether(
name='partparameter',
unique_together=set([]),
),
migrations.RemoveField(
model_name='partparameter',
name='part',
),
migrations.RemoveField(
model_name='partparameter',
name='template',
),
migrations.DeleteModel(
name='CategoryParameterLink',
),
migrations.DeleteModel(
name='PartParameter',
),
migrations.DeleteModel(
name='PartParameterTemplate',
),
]

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 08:12
# Generated by Django 1.11.12 on 2018-04-22 11:53
from __future__ import unicode_literals
from django.db import migrations, models
@ -9,14 +9,14 @@ import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('stock', '0010_stockitem_build'),
('part', '0020_part_salable'),
('stock', '0001_initial'),
('part', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='part',
name='default_location',
field=models.ForeignKey(blank=True, help_text='Where is this item normally stored?', null=True, on_delete=django.db.models.deletion.SET_NULL, to='stock.StockLocation'),
field=models.ForeignKey(blank=True, help_text='Where is this item normally stored?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='stock.StockLocation'),
),
]

View File

@ -1,19 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-12 06:44
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('part', '0002_auto_20180412_0600'),
]
operations = [
migrations.AlterUniqueTogether(
name='part',
unique_together=set([('name', 'category')]),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-13 08:34
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0003_auto_20180412_0644'),
]
operations = [
migrations.AlterField(
model_name='part',
name='category',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='parts', to='part.PartCategory'),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-13 12:30
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0004_auto_20180413_0834'),
]
operations = [
migrations.AlterField(
model_name='partcategory',
name='name',
field=models.CharField(max_length=100, unique=True),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-14 04:03
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0005_auto_20180413_1230'),
]
operations = [
migrations.AlterField(
model_name='part',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='parts', to='part.PartCategory'),
),
]

View File

@ -1,33 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-14 04:16
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0006_auto_20180414_0403'),
]
operations = [
migrations.CreateModel(
name='BomItem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=1, validators=[django.core.validators.MinValueValidator(0)])),
('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part')),
('sub_part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part')),
],
options={
'verbose_name': 'BOM Item',
},
),
migrations.AlterUniqueTogether(
name='bomitem',
unique_together=set([('part', 'sub_part')]),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-14 06:31
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0007_auto_20180414_0416'),
]
operations = [
migrations.AddField(
model_name='part',
name='URL',
field=models.URLField(blank=True),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-14 06:53
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0008_part_url'),
]
operations = [
migrations.AddField(
model_name='part',
name='image',
field=models.ImageField(blank=True, upload_to='part_images'),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-14 07:25
from __future__ import unicode_literals
from django.db import migrations, models
import part.models
class Migration(migrations.Migration):
dependencies = [
('part', '0009_part_image'),
]
operations = [
migrations.AlterField(
model_name='part',
name='image',
field=models.ImageField(blank=True, max_length=255, null=True, upload_to=part.models.rename_part_image),
),
]

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-14 08:21
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
import part.models
class Migration(migrations.Migration):
dependencies = [
('part', '0010_auto_20180414_0725'),
]
operations = [
migrations.CreateModel(
name='PartAttachment',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('attachment', models.FileField(blank=True, null=True, upload_to=part.models.attach_file)),
('part', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='part.Part')),
],
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-14 10:32
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0011_partattachment'),
]
operations = [
migrations.AlterField(
model_name='part',
name='category',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='parts', to='part.PartCategory'),
),
]

View File

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-14 22:38
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0012_auto_20180414_1032'),
]
operations = [
migrations.AlterField(
model_name='part',
name='name',
field=models.CharField(max_length=100, unique=True),
),
migrations.AlterUniqueTogether(
name='part',
unique_together=set([]),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-15 01:07
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0013_auto_20180414_2238'),
]
operations = [
migrations.AlterField(
model_name='partcategory',
name='parent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='part.PartCategory'),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-15 03:02
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0014_auto_20180415_0107'),
]
operations = [
migrations.AlterField(
model_name='partcategory',
name='description',
field=models.CharField(max_length=250),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-15 03:16
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0015_auto_20180415_0302'),
]
operations = [
migrations.AlterField(
model_name='part',
name='description',
field=models.CharField(max_length=250),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-15 14:21
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0016_auto_20180415_0316'),
]
operations = [
migrations.AddField(
model_name='part',
name='purchaseable',
field=models.BooleanField(default=True),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-16 12:08
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0017_part_purchaseable'),
]
operations = [
migrations.AddField(
model_name='part',
name='buildable',
field=models.BooleanField(default=False),
),
]

View File

@ -1,62 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-16 12:49
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0018_part_buildable'),
]
operations = [
migrations.AlterField(
model_name='part',
name='IPN',
field=models.CharField(blank=True, help_text='Internal Part Number', max_length=100),
),
migrations.AlterField(
model_name='part',
name='URL',
field=models.URLField(blank=True, help_text='Link to extenal URL'),
),
migrations.AlterField(
model_name='part',
name='buildable',
field=models.BooleanField(default=False, help_text='Can this part be built from other parts?'),
),
migrations.AlterField(
model_name='part',
name='category',
field=models.ForeignKey(blank=True, help_text='Part category', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='parts', to='part.PartCategory'),
),
migrations.AlterField(
model_name='part',
name='description',
field=models.CharField(help_text='Part description', max_length=250),
),
migrations.AlterField(
model_name='part',
name='minimum_stock',
field=models.PositiveIntegerField(default=0, help_text='Minimum allowed stock level', validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.AlterField(
model_name='part',
name='name',
field=models.CharField(help_text='Part name (must be unique)', max_length=100, unique=True),
),
migrations.AlterField(
model_name='part',
name='purchaseable',
field=models.BooleanField(default=True, help_text='Can this part be purchased from external suppliers?'),
),
migrations.AlterField(
model_name='part',
name='trackable',
field=models.BooleanField(default=False, help_text='Does this part have tracking for unique items?'),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 08:06
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0019_auto_20180416_1249'),
]
operations = [
migrations.AddField(
model_name='part',
name='salable',
field=models.BooleanField(default=False, help_text='Can this part be sold to customers?'),
),
]

View File

@ -1,27 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 08:19
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('company', '0007_auto_20180416_1253'),
('part', '0021_part_default_location'),
]
operations = [
migrations.AddField(
model_name='part',
name='default_supplier',
field=models.ForeignKey(blank=True, help_text='Default supplier part', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='company.SupplierPart'),
),
migrations.AlterField(
model_name='part',
name='default_location',
field=models.ForeignKey(blank=True, help_text='Where is this item normally stored?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='default_parts', to='stock.StockLocation'),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 15:34
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('part', '0022_auto_20180417_0819'),
]
operations = [
migrations.AddField(
model_name='part',
name='notes',
field=models.TextField(blank=True),
),
]

View File

@ -11,7 +11,7 @@ from django.db.models.signals import pre_delete
from django.dispatch import receiver
from InvenTree.models import InvenTreeTree
from company.models import Company
class PartCategory(InvenTreeTree):
""" PartCategory provides hierarchical organization of Part objects.
@ -111,7 +111,7 @@ class Part(models.Model):
related_name='default_parts')
# Default supplier part
default_supplier = models.ForeignKey('company.SupplierPart',
default_supplier = models.ForeignKey('part.SupplierPart',
on_delete=models.SET_NULL,
blank=True, null=True,
help_text='Default supplier part',
@ -352,3 +352,78 @@ class BomItem(models.Model):
par=self.part.name,
child=self.sub_part.name,
n=self.quantity)
class SupplierPart(models.Model):
""" Represents a unique part as provided by a Supplier
Each SupplierPart is identified by a MPN (Manufacturer Part Number)
Each SupplierPart is also linked to a Part object
- A Part may be available from multiple suppliers
"""
def get_absolute_url(self):
return "/supplier-part/{id}/".format(id=self.id)
class Meta:
unique_together = ('part', 'supplier', 'SKU')
# Link to an actual part
# The part will have a field 'supplier_parts' which links to the supplier part options
part = models.ForeignKey(Part, on_delete=models.CASCADE,
related_name='supplier_parts')
supplier = models.ForeignKey(Company, on_delete=models.CASCADE,
related_name='parts')
SKU = models.CharField(max_length=100, help_text='Supplier stock keeping unit')
manufacturer = models.CharField(max_length=100, blank=True, help_text='Manufacturer')
MPN = models.CharField(max_length=100, blank=True, help_text='Manufacturer part number')
URL = models.URLField(blank=True)
description = models.CharField(max_length=250, blank=True)
# Default price for a single unit
single_price = models.DecimalField(max_digits=10, decimal_places=3, default=0)
# Base charge added to order independent of quantity e.g. "Reeling Fee"
base_cost = models.DecimalField(max_digits=10, decimal_places=3, default=0)
# packaging that the part is supplied in, e.g. "Reel"
packaging = models.CharField(max_length=50, blank=True)
# multiple that the part is provided in
multiple = models.PositiveIntegerField(default=1, validators=[MinValueValidator(0)])
# Mimumum number required to order
minimum = models.PositiveIntegerField(default=1, validators=[MinValueValidator(0)])
# lead time for parts that cannot be delivered immediately
lead_time = models.DurationField(blank=True, null=True)
def __str__(self):
return "{sku} - {supplier}".format(
sku=self.SKU,
supplier=self.supplier.name)
class SupplierPriceBreak(models.Model):
""" Represents a quantity price break for a SupplierPart
- Suppliers can offer discounts at larger quantities
- SupplierPart(s) may have zero-or-more associated SupplierPriceBreak(s)
"""
part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='price_breaks')
quantity = models.PositiveIntegerField(validators=[MinValueValidator(0)])
cost = models.DecimalField(max_digits=10, decimal_places=3)
class Meta:
unique_together = ("part", "quantity")
def __str__(self):
return "{mpn} - {cost}{currency} @ {quan}".format(
mpn=self.part.MPN,
cost=self.cost,
currency=self.currency if self.currency else '',
quan=self.quantity)

View File

@ -4,6 +4,19 @@ from django.views.generic.base import RedirectView
from . import views
from . import api
supplier_part_detail_urls = [
url(r'edit/?', views.SupplierPartEdit.as_view(), name='supplier-part-edit'),
url(r'delete/?', views.SupplierPartDelete.as_view(), name='supplier-part-delete'),
url('^.*$', views.SupplierPartDetail.as_view(), name='supplier-part-detail'),
]
supplier_part_urls = [
url(r'^new/?', views.SupplierPartCreate.as_view(), name='supplier-part-create'),
url(r'^(?P<pk>\d+)/', include(supplier_part_detail_urls)),
]
# URL list for part category API
part_cat_api_urls = [

View File

@ -7,8 +7,14 @@ from django.http import HttpResponseRedirect
from django.views.generic import DetailView, ListView
from django.views.generic.edit import UpdateView, DeleteView, CreateView
from .forms import EditPartForm, EditCategoryForm, EditBomItemForm
from .models import PartCategory, Part, BomItem
from .models import SupplierPart
from .forms import EditPartForm
from .forms import EditCategoryForm
from .forms import EditBomItemForm
from .forms import EditSupplierPartForm
class PartIndex(ListView):
@ -191,3 +197,53 @@ class BomItemDelete(DeleteView):
return super(BomItemDelete, self).post(request, *args, **kwargs)
else:
return HttpResponseRedirect(self.get_object().get_absolute_url())
class SupplierPartDetail(DetailView):
model = SupplierPart
template_name = 'company/partdetail.html'
context_object_name = 'part'
queryset = SupplierPart.objects.all()
class SupplierPartEdit(UpdateView):
model = SupplierPart
template_name = 'company/partedit.html'
context_object_name = 'part'
form_class = EditSupplierPartForm
class SupplierPartCreate(CreateView):
model = SupplierPart
form_class = EditSupplierPartForm
template_name = 'company/partcreate.html'
context_object_name = 'part'
def get_initial(self):
initials = super(SupplierPartCreate, self).get_initial().copy()
supplier_id = self.request.GET.get('supplier', None)
part_id = self.request.GET.get('part', None)
if supplier_id:
initials['supplier'] = get_object_or_404(Supplier, pk=supplier_id)
# TODO
# self.fields['supplier'].disabled = True
if part_id:
initials['part'] = get_object_or_404(Part, pk=part_id)
# TODO
# self.fields['part'].disabled = True
return initials
class SupplierPartDelete(DeleteView):
model = SupplierPart
success_url = '/supplier/'
template_name = 'company/partdelete.html'
def post(self, request, *args, **kwargs):
if 'confirm' in request.POST:
return super(SupplierPartDelete, self).post(request, *args, **kwargs)
else:
return HttpResponseRedirect(self.get_object().get_absolute_url())

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-12 05:02
# Generated by Django 1.11.12 on 2018-04-22 11:53
from __future__ import unicode_literals
from django.conf import settings
@ -15,54 +15,47 @@ class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('part', '0001_initial'),
('supplier', '__first__'),
('company', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='HistoricalStockItem',
fields=[
('id', models.IntegerField(auto_created=True, blank=True, db_index=True, verbose_name='ID')),
('quantity', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(0)])),
('updated', models.DateField(blank=True, editable=False)),
('stocktake_date', models.DateField(blank=True, null=True)),
('review_needed', models.BooleanField(default=False)),
('status', models.PositiveIntegerField(choices=[(10, 'In stock'), (15, 'Incoming'), (50, 'Attention needed'), (20, 'In progress'), (55, 'Damaged'), (25, 'Complete'), (60, 'Destroyed')], default=10, validators=[django.core.validators.MinValueValidator(0)])),
('notes', models.CharField(blank=True, max_length=100)),
('expected_arrival', models.DateField(blank=True, null=True)),
('infinite', models.BooleanField(default=False)),
('history_id', models.AutoField(primary_key=True, serialize=False)),
('history_date', models.DateTimeField()),
('history_type', models.CharField(choices=[('+', 'Created'), ('~', 'Changed'), ('-', 'Deleted')], max_length=1)),
('history_user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)),
],
options={
'ordering': ('-history_date', '-history_id'),
'get_latest_by': 'history_date',
'verbose_name': 'historical stock item',
},
),
migrations.CreateModel(
name='StockItem',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('serial', models.PositiveIntegerField(blank=True, help_text='Serial number for this item', null=True)),
('URL', models.URLField(blank=True, max_length=125)),
('batch', models.CharField(blank=True, help_text='Batch code for this stock item', max_length=100)),
('quantity', models.PositiveIntegerField(validators=[django.core.validators.MinValueValidator(0)])),
('updated', models.DateField(auto_now=True)),
('stocktake_date', models.DateField(blank=True, null=True)),
('review_needed', models.BooleanField(default=False)),
('status', models.PositiveIntegerField(choices=[(10, 'In stock'), (15, 'Incoming'), (50, 'Attention needed'), (20, 'In progress'), (55, 'Damaged'), (25, 'Complete'), (60, 'Destroyed')], default=10, validators=[django.core.validators.MinValueValidator(0)])),
('notes', models.CharField(blank=True, max_length=100)),
('expected_arrival', models.DateField(blank=True, null=True)),
('status', models.PositiveIntegerField(choices=[(10, 'OK'), (60, 'Destroyed'), (50, 'Attention needed'), (55, 'Damaged')], default=10, validators=[django.core.validators.MinValueValidator(0)])),
('notes', models.TextField(blank=True)),
('infinite', models.BooleanField(default=False)),
('belongs_to', models.ForeignKey(blank=True, help_text='Is this item installed in another item?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='owned_parts', to='stock.StockItem')),
('customer', models.ForeignKey(blank=True, help_text='Item assigned to customer?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stockitems', to='company.Company')),
],
),
migrations.CreateModel(
name='StockItemTracking',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(auto_now_add=True)),
('title', models.CharField(max_length=250)),
('notes', models.TextField(blank=True)),
('system', models.BooleanField(default=False)),
('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tracking_info', to='stock.StockItem')),
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='StockLocation',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=100)),
('description', models.CharField(blank=True, max_length=250)),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='stock.StockLocation')),
('name', models.CharField(max_length=100, unique=True)),
('description', models.CharField(max_length=250)),
('parent', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='stock.StockLocation')),
],
options={
'abstract': False,
@ -71,7 +64,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='stockitem',
name='location',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='stock.StockLocation'),
field=models.ForeignKey(blank=True, help_text='Where is this stock item located?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='items', to='stock.StockLocation'),
),
migrations.AddField(
model_name='stockitem',
@ -86,30 +79,14 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='stockitem',
name='supplier_part',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='supplier.SupplierPart'),
),
migrations.AddField(
model_name='historicalstockitem',
name='location',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='stock.StockLocation'),
),
migrations.AddField(
model_name='historicalstockitem',
name='part',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='part.Part'),
),
migrations.AddField(
model_name='historicalstockitem',
name='stocktake_user',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to=settings.AUTH_USER_MODEL),
),
migrations.AddField(
model_name='historicalstockitem',
name='supplier_part',
field=models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='+', to='supplier.SupplierPart'),
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='part.SupplierPart'),
),
migrations.AlterUniqueTogether(
name='stocklocation',
unique_together=set([('name', 'parent')]),
),
migrations.AlterUniqueTogether(
name='stockitem',
unique_together=set([('part', 'serial')]),
),
]

View File

@ -1,34 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-12 06:22
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='historicalstockitem',
name='expected_arrival',
),
migrations.RemoveField(
model_name='stockitem',
name='expected_arrival',
),
migrations.AlterField(
model_name='historicalstockitem',
name='status',
field=models.PositiveIntegerField(choices=[(10, 'OK'), (60, 'Destroyed'), (50, 'Attention needed'), (55, 'Damaged')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
),
migrations.AlterField(
model_name='stockitem',
name='status',
field=models.PositiveIntegerField(choices=[(10, 'OK'), (60, 'Destroyed'), (50, 'Attention needed'), (55, 'Damaged')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-13 12:30
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0002_auto_20180412_0622'),
]
operations = [
migrations.AlterField(
model_name='stocklocation',
name='name',
field=models.CharField(max_length=100, unique=True),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-14 10:32
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('stock', '0003_auto_20180413_1230'),
]
operations = [
migrations.AlterField(
model_name='stockitem',
name='location',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='items', to='stock.StockLocation'),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-15 01:07
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('stock', '0004_auto_20180414_1032'),
]
operations = [
migrations.AlterField(
model_name='stocklocation',
name='parent',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='children', to='stock.StockLocation'),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-15 03:02
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0005_auto_20180415_0107'),
]
operations = [
migrations.AlterField(
model_name='stocklocation',
name='description',
field=models.CharField(max_length=250),
),
]

View File

@ -1,81 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-16 08:53
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('supplier', '0006_auto_20180415_1011'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('stock', '0006_auto_20180415_0302'),
]
operations = [
migrations.CreateModel(
name='StockItemTracking',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date', models.DateField(auto_now_add=True)),
('title', models.CharField(max_length=250)),
('description', models.CharField(blank=True, max_length=1024)),
],
),
migrations.RemoveField(
model_name='historicalstockitem',
name='history_user',
),
migrations.RemoveField(
model_name='historicalstockitem',
name='location',
),
migrations.RemoveField(
model_name='historicalstockitem',
name='part',
),
migrations.RemoveField(
model_name='historicalstockitem',
name='stocktake_user',
),
migrations.RemoveField(
model_name='historicalstockitem',
name='supplier_part',
),
migrations.AddField(
model_name='stockitem',
name='batch',
field=models.CharField(blank=True, max_length=100),
),
migrations.AddField(
model_name='stockitem',
name='belongs_to',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='owned_parts', to='stock.StockItem'),
),
migrations.AddField(
model_name='stockitem',
name='customer',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stockitems', to='supplier.Customer'),
),
migrations.AddField(
model_name='stockitem',
name='serial',
field=models.PositiveIntegerField(blank=True, null=True),
),
migrations.DeleteModel(
name='HistoricalStockItem',
),
migrations.AddField(
model_name='stockitemtracking',
name='item',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tracking_info', to='stock.StockItem'),
),
migrations.AddField(
model_name='stockitemtracking',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-16 11:05
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0007_auto_20180416_0853'),
]
operations = [
migrations.AddField(
model_name='stockitem',
name='URL',
field=models.URLField(blank=True, max_length=125),
),
]

View File

@ -1,46 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-16 12:53
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('part', '0019_auto_20180416_1249'),
('stock', '0008_stockitem_url'),
]
operations = [
migrations.AlterField(
model_name='stockitem',
name='batch',
field=models.CharField(blank=True, help_text='Batch code for this stock item', max_length=100),
),
migrations.AlterField(
model_name='stockitem',
name='belongs_to',
field=models.ForeignKey(blank=True, help_text='Is this item installed in another item?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='owned_parts', to='stock.StockItem'),
),
migrations.AlterField(
model_name='stockitem',
name='customer',
field=models.ForeignKey(blank=True, help_text='Item assigned to customer?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stockitems', to='supplier.Customer'),
),
migrations.AlterField(
model_name='stockitem',
name='location',
field=models.ForeignKey(blank=True, help_text='Where is this stock item located?', null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='items', to='stock.StockLocation'),
),
migrations.AlterField(
model_name='stockitem',
name='serial',
field=models.PositiveIntegerField(blank=True, help_text='Serial number for this item', null=True),
),
migrations.AlterUniqueTogether(
name='stockitem',
unique_together=set([('part', 'serial')]),
),
]

View File

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-16 14:03
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('build', '0001_initial'),
('stock', '0009_auto_20180416_1253'),
]
operations = [
migrations.AddField(
model_name='stockitem',
name='build',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='build.Build'),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-17 11:27
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0010_stockitem_build'),
]
operations = [
migrations.AlterField(
model_name='stockitem',
name='status',
field=models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 13:16
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0011_auto_20180417_2127'),
]
operations = [
migrations.AlterField(
model_name='stockitem',
name='status',
field=models.PositiveIntegerField(choices=[(10, 'OK'), (60, 'Destroyed'), (50, 'Attention needed'), (55, 'Damaged')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 13:37
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('stock', '0012_auto_20180417_1316'),
]
operations = [
migrations.AlterField(
model_name='stockitem',
name='customer',
field=models.ForeignKey(blank=True, help_text='Item assigned to customer?', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stockitems', to='supplier.Supplier'),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-17 14:28
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0013_auto_20180417_1337'),
]
operations = [
migrations.AlterField(
model_name='stockitem',
name='status',
field=models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 15:14
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0014_auto_20180418_0028'),
]
operations = [
migrations.AlterField(
model_name='stockitem',
name='status',
field=models.PositiveIntegerField(choices=[(10, 'OK'), (60, 'Destroyed'), (50, 'Attention needed'), (55, 'Damaged')], default=10, validators=[django.core.validators.MinValueValidator(0)]),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 15:16
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0015_auto_20180417_1514'),
]
operations = [
migrations.AlterField(
model_name='stockitem',
name='notes',
field=models.TextField(blank=True),
),
]

View File

@ -1,24 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 15:36
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0016_auto_20180417_1516'),
]
operations = [
migrations.RemoveField(
model_name='stockitemtracking',
name='description',
),
migrations.AddField(
model_name='stockitemtracking',
name='notes',
field=models.TextField(blank=True),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.12 on 2018-04-17 22:43
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('stock', '0017_auto_20180417_1536'),
]
operations = [
migrations.AddField(
model_name='stockitemtracking',
name='system',
field=models.BooleanField(default=False),
),
]

View File

@ -11,10 +11,11 @@ from django.dispatch import receiver
from datetime import datetime
from company.models import SupplierPart
from part.models import Part
#from company.models import Company
#from part.models import Part
#from part.models import SupplierPart
from InvenTree.models import InvenTreeTree
from build.models import Build
#from build.models import Build
class StockLocation(InvenTreeTree):
@ -66,10 +67,10 @@ class StockItem(models.Model):
]
# The 'master' copy of the part of which this stock item is an instance
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='locations')
part = models.ForeignKey('part.Part', on_delete=models.CASCADE, related_name='locations')
# The 'supplier part' used in this instance. May be null if no supplier parts are defined the master part
supplier_part = models.ForeignKey(SupplierPart, blank=True, null=True, on_delete=models.SET_NULL)
supplier_part = models.ForeignKey('part.SupplierPart', blank=True, null=True, on_delete=models.SET_NULL)
# Where the part is stored. If the part has been used to build another stock item, the location may not make sense
location = models.ForeignKey(StockLocation, on_delete=models.DO_NOTHING,
@ -98,7 +99,7 @@ class StockItem(models.Model):
help_text='Batch code for this stock item')
# If this part was produced by a build, point to that build here
build = models.ForeignKey(Build, on_delete=models.SET_NULL, blank=True, null=True)
# build = models.ForeignKey('build.Build', on_delete=models.SET_NULL, blank=True, null=True)
# Quantity of this stock item. Value may be overridden by other settings
quantity = models.PositiveIntegerField(validators=[MinValueValidator(0)])

View File

@ -9,7 +9,6 @@
<li><a href="{% url 'part-index' %}">Parts</a></li>
<li><a href="{% url 'stock-index' %}">Stock</a></li>
<li><a href="{% url 'build-index' %}">Build</a></li>
<li><a href="{% url 'supplier-index' %}">Suppliers</a></li>
</ul>
</div>
</nav>

View File

@ -15,9 +15,9 @@ test:
python InvenTree/manage.py test --noinput
migrate:
python InvenTree/manage.py makemigrations company
python InvenTree/manage.py makemigrations part
python InvenTree/manage.py makemigrations stock
python InvenTree/manage.py makemigrations company
python InvenTree/manage.py makemigrations build
python InvenTree/manage.py migrate --run-syncdb
python InvenTree/manage.py check