Added ManufacturerPart model, form and views

This commit is contained in:
eeintech 2021-03-23 17:01:54 -04:00
parent c9464fd393
commit e897864396
3 changed files with 255 additions and 29 deletions

View File

@ -17,6 +17,7 @@ from djmoney.forms.fields import MoneyField
import common.settings import common.settings
from .models import Company from .models import Company
from .models import ManufacturerPart
from .models import SupplierPart from .models import SupplierPart
from .models import SupplierPriceBreak from .models import SupplierPriceBreak
@ -84,12 +85,30 @@ class CompanyImageDownloadForm(HelperForm):
] ]
class EditManufacturerPartForm(HelperForm):
""" Form for editing a ManufacturerPart object """
field_prefix = {
'link': 'fa-link',
'MPN': 'fa-hashtag',
}
class Meta:
model = ManufacturerPart
fields = [
'part',
'description',
'manufacturer',
'MPN',
'link',
]
class EditSupplierPartForm(HelperForm): class EditSupplierPartForm(HelperForm):
""" Form for editing a SupplierPart object """ """ Form for editing a SupplierPart object """
field_prefix = { field_prefix = {
'link': 'fa-link', 'link': 'fa-link',
'MPN': 'fa-hashtag',
'SKU': 'fa-hashtag', 'SKU': 'fa-hashtag',
'note': 'fa-pencil-alt', 'note': 'fa-pencil-alt',
} }
@ -110,8 +129,6 @@ class EditSupplierPartForm(HelperForm):
'supplier', 'supplier',
'SKU', 'SKU',
'description', 'description',
'manufacturer',
'MPN',
'link', 'link',
'note', 'note',
'single_pricing', 'single_pricing',

View File

@ -278,19 +278,75 @@ class Contact(models.Model):
on_delete=models.CASCADE) on_delete=models.CASCADE)
class SupplierPart(models.Model): class MaufacturerPart(models.Model):
""" Represents a unique part as provided by a Supplier """ Represents a unique part as provided by a Manufacturer
Each SupplierPart is identified by a MPN (Manufacturer Part Number) Each MaufacturerPart is identified by a MPN (Manufacturer Part Number)
Each SupplierPart is also linked to a Part object. Each MaufacturerPart is also linked to a Part object.
A Part may be available from multiple suppliers A Part may be available from multiple manufacturers
Attributes: Attributes:
part: Link to the master Part part: Link to the master Part
manufacturer: Company that manufactures the MaufacturerPart
MPN: Manufacture part number
link: Link to external website for this manufacturer part
description: Descriptive notes field
"""
class Meta:
unique_together = ('part', 'manufacturer', 'MPN')
part = models.ForeignKey('part.Part', on_delete=models.CASCADE,
related_name='manufacturer_parts',
verbose_name=_('Base Part'),
limit_choices_to={
'purchaseable': True,
},
help_text=_('Select part'),
)
manufacturer = models.ForeignKey(
Company,
on_delete=models.SET_NULL,
related_name='manufactured_parts',
limit_choices_to={
'is_manufacturer': True
},
verbose_name=_('Manufacturer'),
help_text=_('Select manufacturer'),
null=True, blank=True
)
MPN = models.CharField(
max_length=100, blank=True, null=True,
verbose_name=_('MPN'),
help_text=_('Manufacturer Part Number')
)
link = InvenTreeURLField(
blank=True, null=True,
verbose_name=_('Link'),
help_text=_('URL for external manufacturer part link')
)
description = models.CharField(
max_length=250, blank=True, null=True,
verbose_name=_('Description'),
help_text=_('Manufacturer part description')
)
class SupplierPart(models.Model):
""" Represents a unique part as provided by a Supplier
Each SupplierPart is identified by a SKU (Supplier Part Number)
Each SupplierPart is also linked to a Part or ManufacturerPart object.
A Part may be available from multiple suppliers
Attributes:
part_type: Part or ManufacturerPart
part_id: Part or ManufacturerPart ID
supplier: Company that supplies this SupplierPart object supplier: Company that supplies this SupplierPart object
SKU: Stock keeping unit (supplier part number) SKU: Stock keeping unit (supplier part number)
manufacturer: Company that manufactures the SupplierPart (leave blank if it is the sample as the Supplier!) link: Link to external website for this supplier part
MPN: Manufacture part number
link: Link to external website for this part
description: Descriptive notes field description: Descriptive notes field
note: Longer form note field note: Longer form note field
base_cost: Base charge added to order independent of quantity e.g. "Reeling Fee" base_cost: Base charge added to order independent of quantity e.g. "Reeling Fee"
@ -330,24 +386,6 @@ class SupplierPart(models.Model):
help_text=_('Supplier stock keeping unit') help_text=_('Supplier stock keeping unit')
) )
manufacturer = models.ForeignKey(
Company,
on_delete=models.SET_NULL,
related_name='manufactured_parts',
limit_choices_to={
'is_manufacturer': True
},
verbose_name=_('Manufacturer'),
help_text=_('Select manufacturer'),
null=True, blank=True
)
MPN = models.CharField(
max_length=100, blank=True, null=True,
verbose_name=_('MPN'),
help_text=_('Manufacturer part number')
)
link = InvenTreeURLField( link = InvenTreeURLField(
blank=True, null=True, blank=True, null=True,
verbose_name=_('Link'), verbose_name=_('Link'),

View File

@ -24,6 +24,7 @@ from InvenTree.helpers import str2bool
from InvenTree.views import InvenTreeRoleMixin from InvenTree.views import InvenTreeRoleMixin
from .models import Company from .models import Company
from .models import ManufacturerPart
from .models import SupplierPart from .models import SupplierPart
from .models import SupplierPriceBreak from .models import SupplierPriceBreak
@ -31,6 +32,7 @@ from part.models import Part
from .forms import EditCompanyForm from .forms import EditCompanyForm
from .forms import CompanyImageForm from .forms import CompanyImageForm
from .forms import EditManufacturerPartForm
from .forms import EditSupplierPartForm from .forms import EditSupplierPartForm
from .forms import EditPriceBreakForm from .forms import EditPriceBreakForm
from .forms import CompanyImageDownloadForm from .forms import CompanyImageDownloadForm
@ -331,6 +333,175 @@ class CompanyDelete(AjaxDeleteView):
} }
class ManufacturerPartDetail(DetailView):
""" Detail view for ManufacturerPart """
model = ManufacturerPart
template_name = 'company/manufacturer_part_detail.html'
context_object_name = 'part'
queryset = ManufacturerPart.objects.all()
permission_required = 'purchase_order.view'
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
return ctx
class ManufacturerPartEdit(AjaxUpdateView):
""" Update view for editing ManufacturerPart """
model = ManufacturerPart
context_object_name = 'part'
form_class = EditSupplierPartForm
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Edit Manufacturer Part')
class ManufacturerPartCreate(AjaxCreateView):
""" Create view for making new ManufacturerPart """
model = ManufacturerPart
form_class = EditManufacturerPartForm
ajax_template_name = 'company/manufacturer_part_create.html'
ajax_form_title = _('Create New Manufacturer Part')
context_object_name = 'part'
def get_context_data(self):
"""
Supply context data to the form
"""
ctx = super().get_context_data()
# Add 'part' object
form = self.get_form()
part = form['part'].value()
try:
part = Part.objects.get(pk=part)
except (ValueError, Part.DoesNotExist):
part = None
ctx['part'] = part
return ctx
def get_form(self):
""" Create Form instance to create a new ManufacturerPart object.
Hide some fields if they are not appropriate in context
"""
form = super(AjaxCreateView, self).get_form()
if form.initial.get('part', None):
# Hide the part field
form.fields['part'].widget = HiddenInput()
return form
def get_initial(self):
""" Provide initial data for new ManufacturerPart:
- If 'manufacturer_id' provided, pre-fill manufacturer field
- If 'part_id' provided, pre-fill part field
"""
initials = super(SupplierPartCreate, self).get_initial().copy()
manufacturer_id = self.get_param('manufacturer')
part_id = self.get_param('part')
if manufacturer_id:
try:
initials['manufacturer'] = Company.objects.get(pk=manufacturer_id)
except (ValueError, Company.DoesNotExist):
pass
if part_id:
try:
initials['part'] = Part.objects.get(pk=part_id)
except (ValueError, Part.DoesNotExist):
pass
class ManufacturerPartDelete(AjaxDeleteView):
""" Delete view for removing a ManufacturerPart.
ManufacturerParts can be deleted using a variety of 'selectors'.
- ?part=<pk> -> Delete a single ManufacturerPart object
- ?parts=[] -> Delete a list of ManufacturerPart objects
"""
success_url = '/manufacturer/'
ajax_template_name = 'company/partdelete.html'
ajax_form_title = _('Delete Manufacturer Part')
role_required = 'purchase_order.delete'
parts = []
def get_context_data(self):
ctx = {}
ctx['parts'] = self.parts
return ctx
def get_parts(self):
""" Determine which ManufacturerPart object(s) the user wishes to delete.
"""
self.parts = []
# User passes a single ManufacturerPart ID
if 'part' in self.request.GET:
try:
self.parts.append(ManufacturerPart.objects.get(pk=self.request.GET.get('part')))
except (ValueError, SupplierPart.DoesNotExist):
pass
elif 'parts[]' in self.request.GET:
part_id_list = self.request.GET.getlist('parts[]')
self.parts = ManufacturerPart.objects.filter(id__in=part_id_list)
def get(self, request, *args, **kwargs):
self.request = request
self.get_parts()
return self.renderJsonResponse(request, form=self.get_form())
def post(self, request, *args, **kwargs):
""" Handle the POST action for deleting ManufacturerPart object.
"""
self.request = request
self.parts = []
for item in self.request.POST:
if item.startswith('manufacturer-part-'):
pk = item.replace('manufacturer-part-', '')
try:
self.parts.append(ManufacturerPart.objects.get(pk=pk))
except (ValueError, ManufacturerPart.DoesNotExist):
pass
confirm = str2bool(self.request.POST.get('confirm_delete', False))
data = {
'form_valid': confirm,
}
if confirm:
for part in self.parts:
part.delete()
return self.renderJsonResponse(self.request, data=data, form=self.get_form())
class SupplierPartDetail(DetailView): class SupplierPartDetail(DetailView):
""" Detail view for SupplierPart """ """ Detail view for SupplierPart """
model = SupplierPart model = SupplierPart