diff --git a/InvenTree/order/migrations/0020_auto_20200420_0940.py b/InvenTree/order/migrations/0020_auto_20200420_0940.py new file mode 100644 index 0000000000..59431353dd --- /dev/null +++ b/InvenTree/order/migrations/0020_auto_20200420_0940.py @@ -0,0 +1,76 @@ +# Generated by Django 3.0.5 on 2020-04-20 09:40 + +import InvenTree.fields +import InvenTree.models +from django.conf import settings +import django.core.validators +from django.db import migrations, models +import django.db.models.deletion +import markdownx.models + + +class Migration(migrations.Migration): + + dependencies = [ + ('company', '0021_remove_supplierpart_manufacturer_name'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('order', '0019_purchaseorder_supplier_reference'), + ] + + operations = [ + migrations.CreateModel( + name='SalesOrder', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('reference', models.CharField(help_text='Order reference', max_length=64, unique=True)), + ('description', models.CharField(help_text='Order description', max_length=250)), + ('link', models.URLField(blank=True, help_text='Link to external page')), + ('creation_date', models.DateField(blank=True, null=True)), + ('status', models.PositiveIntegerField(choices=[(10, 'Pending'), (20, 'Placed'), (30, 'Complete'), (40, 'Cancelled'), (50, 'Lost'), (60, 'Returned')], default=10, help_text='Order status')), + ('issue_date', models.DateField(blank=True, null=True)), + ('complete_date', models.DateField(blank=True, null=True)), + ('notes', markdownx.models.MarkdownxField(blank=True, help_text='Order notes')), + ('customer_reference', models.CharField(blank=True, help_text='Customer order reference code', max_length=64)), + ('created_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to=settings.AUTH_USER_MODEL)), + ('customer', models.ForeignKey(help_text='Customer', limit_choices_to={True, 'is_supplier'}, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sales_orders', to='company.Company')), + ], + options={ + 'abstract': False, + }, + ), + migrations.AlterField( + model_name='purchaseorder', + name='supplier', + field=models.ForeignKey(help_text='Supplier', limit_choices_to={'is_supplier': True}, on_delete=django.db.models.deletion.CASCADE, related_name='purchase_orders', to='company.Company'), + ), + migrations.AlterField( + model_name='purchaseorder', + name='supplier_reference', + field=models.CharField(blank=True, help_text='Supplier order reference code', max_length=64), + ), + migrations.CreateModel( + name='SalesOrderLineItem', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('quantity', InvenTree.fields.RoundingDecimalField(decimal_places=5, default=1, help_text='Item quantity', max_digits=15, validators=[django.core.validators.MinValueValidator(0)])), + ('reference', models.CharField(blank=True, help_text='Line item reference', max_length=100)), + ('notes', models.CharField(blank=True, help_text='Line item notes', max_length=500)), + ('order', models.ForeignKey(help_text='Sales Order', on_delete=django.db.models.deletion.CASCADE, related_name='lines', to='order.SalesOrder')), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='SalesOrderAttachment', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('attachment', models.FileField(help_text='Select file to attach', upload_to=InvenTree.models.rename_attachment)), + ('comment', models.CharField(help_text='File comment', max_length=100)), + ('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='order.SalesOrder')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 3a7d65abac..77ea885b5c 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -117,6 +117,7 @@ class PurchaseOrder(Order): Attributes: supplier: Reference to the company supplying the goods in the order + supplier_reference: Optional field for supplier order reference code received_by: User that received the goods """ @@ -128,10 +129,10 @@ class PurchaseOrder(Order): 'is_supplier': True, }, related_name='purchase_orders', - help_text=_('Company') + help_text=_('Supplier') ) - supplier_reference = models.CharField(max_length=64, blank=True, help_text=_("Supplier order reference")) + supplier_reference = models.CharField(max_length=64, blank=True, help_text=_("Supplier order reference code")) received_by = models.ForeignKey( User, @@ -244,6 +245,26 @@ class PurchaseOrder(Order): self.complete_order() # This will save the model +class SalesOrder(Order): + """ + A SalesOrder represents a list of goods shipped outwards to a customer. + + Attributes: + customer: Reference to the company receiving the goods in the order + customer_reference: Optional field for customer order reference code + """ + + customer = models.ForeignKey(Company, + on_delete=models.SET_NULL, + null=True, + limit_choices_to={'is_supplier', True}, + related_name='sales_orders', + help_text=_("Customer"), + ) + + customer_reference = models.CharField(max_length=64, blank=True, help_text=_("Customer order reference code")) + + class PurchaseOrderAttachment(InvenTreeAttachment): """ Model for storing file attachments against a PurchaseOrder object @@ -255,6 +276,17 @@ class PurchaseOrderAttachment(InvenTreeAttachment): order = models.ForeignKey(PurchaseOrder, on_delete=models.CASCADE, related_name="attachments") +class SalesOrderAttachment(InvenTreeAttachment): + """ + Model for storing file attachments against a SalesOrder object + """ + + def getSubDir(self): + return os.path.join("so_files", str(self.order.id)) + + order = models.ForeignKey(SalesOrder, on_delete=models.CASCADE, related_name='attachments') + + class OrderLineItem(models.Model): """ Abstract model for an order line item @@ -315,3 +347,13 @@ class PurchaseOrderLineItem(OrderLineItem): """ Calculate the number of items remaining to be received """ r = self.quantity - self.received return max(r, 0) + + +class SalesOrderLineItem(OrderLineItem): + """ + Model for a single LineItem in a SalesOrder + """ + + order = models.ForeignKey(SalesOrder, on_delete=models.CASCADE, related_name='lines', help_text=_('Sales Order')) + + # TODO - Add link for part items