- lots of changes to snippets - lots of work left to do but this commit is getting hugemaster
parent
311c8dc2fd
commit
3d1bbef4cb
@ -0,0 +1,198 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from modelcluster.models import ClusterableModel
|
||||
from modelcluster.fields import ParentalKey
|
||||
from wagtail.documents.models import Document
|
||||
from wagtail.models import Orderable
|
||||
from wagtail.search import index
|
||||
from sf_auth.models import User
|
||||
from .client import Company
|
||||
from .product import LandingPostType
|
||||
from .project import SlabConstruction
|
||||
from .location import STATES
|
||||
|
||||
|
||||
class SalesRep(models.Model):
|
||||
name = models.CharField(max_length=255)
|
||||
user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Bid(index.Indexed, ClusterableModel):
|
||||
number = models.CharField(
|
||||
max_length=16,
|
||||
unique=True,
|
||||
help_text='not including revision, e.g. "23-100"'
|
||||
)
|
||||
name = models.CharField(max_length=255)
|
||||
estimator = models.ForeignKey(
|
||||
User,
|
||||
on_delete=models.PROTECT,
|
||||
limit_choices_to={'groups__name__in': ['staff estimator']},
|
||||
)
|
||||
sales_rep = models.ForeignKey(SalesRep, on_delete=models.PROTECT)
|
||||
city = models.CharField(max_length=255, blank=True, null=True)
|
||||
province = models.CharField(
|
||||
'state',
|
||||
max_length=255,
|
||||
blank=True,
|
||||
null=True,
|
||||
choices=STATES,
|
||||
)
|
||||
client = models.ForeignKey(
|
||||
Company,
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='client_bids',
|
||||
)
|
||||
contractor = models.ForeignKey(
|
||||
Company,
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='contractor_bids',
|
||||
)
|
||||
architect = models.ForeignKey(
|
||||
Company,
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='architect_bids',
|
||||
)
|
||||
engineer = models.ForeignKey(
|
||||
Company,
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name='engineer_bids',
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.number} {self.name}'
|
||||
|
||||
|
||||
class BidDocument(Orderable, ClusterableModel):
|
||||
bid = ParentalKey(Bid, on_delete=models.CASCADE, related_name='documents')
|
||||
doc = models.ForeignKey(Document, on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return self.doc.title
|
||||
|
||||
def name(self):
|
||||
return self.doc.title
|
||||
|
||||
|
||||
class BidRevision(Orderable, ClusterableModel):
|
||||
bid = ParentalKey(Bid, on_delete=models.PROTECT, related_name='revisions')
|
||||
revision = models.IntegerField()
|
||||
date = models.DateField()
|
||||
installed = models.BooleanField()
|
||||
|
||||
def __str__(self):
|
||||
return f'{self.bid.number} R{self.revision}'
|
||||
|
||||
def name(self):
|
||||
return self.revision
|
||||
|
||||
|
||||
class BidScope(Orderable, ClusterableModel):
|
||||
revision = ParentalKey(BidRevision, on_delete=models.PROTECT, related_name='scopes')
|
||||
name = models.CharField(max_length=255)
|
||||
dwg_ref = models.CharField(
|
||||
'drawing reference',
|
||||
max_length=255,
|
||||
blank=True,
|
||||
null=True,
|
||||
help_text='e.g. A401-1',
|
||||
)
|
||||
grid = models.CharField(max_length=16, blank=True, null=True)
|
||||
slab_construction = models.ForeignKey(SlabConstruction, on_delete=models.PROTECT)
|
||||
|
||||
|
||||
class TakeoffFloor(Orderable, ClusterableModel):
|
||||
scope = ParentalKey(BidScope, on_delete=models.CASCADE, related_name='floors')
|
||||
name = models.CharField(max_length=255, help_text='name, number, or designation')
|
||||
|
||||
risers = models.IntegerField(help_text='count', blank=True, default=0)
|
||||
flights = models.IntegerField(help_text='count', blank=True, default=0)
|
||||
ex_treads = models.IntegerField(
|
||||
'extended treads',
|
||||
help_text='count',
|
||||
blank=True,
|
||||
default=0,
|
||||
)
|
||||
haunches = models.IntegerField(help_text='count', blank=True, default=0)
|
||||
|
||||
cane_rail = models.IntegerField(help_text='linear feet', blank=True, default=0)
|
||||
guard_rail = models.IntegerField(help_text='linear feet', blank=True, default=0)
|
||||
center_rail = models.IntegerField(help_text='linear feet', blank=True, default=0)
|
||||
ex_sr = models.IntegerField(
|
||||
'extra stair rail',
|
||||
help_text='linear feet, replaces wall rail',
|
||||
blank=True,
|
||||
default=0,
|
||||
)
|
||||
ex_wr = models.IntegerField(
|
||||
'extra wall rail',
|
||||
help_text='linear feet, replaces stair rail',
|
||||
blank=True,
|
||||
default=0,
|
||||
)
|
||||
add_sr = models.IntegerField(
|
||||
'additional stair rail',
|
||||
help_text='linear feet, e.g. at CIP, ramps, etc',
|
||||
blank=True,
|
||||
default=0,
|
||||
)
|
||||
add_wr = models.IntegerField(
|
||||
'additional wall rail',
|
||||
help_text='linear feet, e.g. at CIP, ramps, etc',
|
||||
blank=True,
|
||||
default=0,
|
||||
)
|
||||
|
||||
gates = models.IntegerField(help_text='count', blank=True, default=0)
|
||||
standpipes = models.IntegerField(help_text='count', blank=True, default=0)
|
||||
|
||||
|
||||
class TakeoffLanding(Orderable, ClusterableModel):
|
||||
floor = ParentalKey(TakeoffFloor, on_delete=models.CASCADE, related_name='landings')
|
||||
width = models.IntegerField(help_text='inches')
|
||||
depth = models.IntegerField(help_text='inches')
|
||||
post_count = models.IntegerField(blank=True, default=0)
|
||||
post_type = models.ForeignKey(
|
||||
LandingPostType,
|
||||
on_delete=models.PROTECT,
|
||||
blank=True,
|
||||
null=True,
|
||||
)
|
||||
post_length = models.IntegerField(
|
||||
help_text='each post, linear feet',
|
||||
blank=True,
|
||||
default=0,
|
||||
)
|
||||
|
||||
def clean(self):
|
||||
if self.post_count > 0 and self.post_type is None:
|
||||
raise ValidationError('Post type required when post count > 0')
|
||||
|
||||
def scope(self):
|
||||
return self.floor.scope
|
||||
|
||||
# derived properties
|
||||
|
||||
def area_sf(self):
|
||||
return (self.width / 12.0) * (self.depth / 12.0)
|
||||
|
||||
def embed_count(self):
|
||||
if self.scope.slab_construction.uses_embeds:
|
||||
return 4
|
||||
return 0
|
||||
|
||||
def hanger_plate_count(self):
|
||||
if self.scope.slab_construction.uses_hangers:
|
||||
return 4 - self.post_count
|
||||
return 0
|
File diff suppressed because one or more lines are too long
Loading…
Reference in new issue