Skip to content

Instantly share code, notes, and snippets.

@shofetim
Created March 7, 2013 22:33
Show Gist options
  • Select an option

  • Save shofetim/5112457 to your computer and use it in GitHub Desktop.

Select an option

Save shofetim/5112457 to your computer and use it in GitHub Desktop.
from __future__ import unicode_literals
from datetime import datetime, timedelta
from django.conf import settings
from django.db import models
from django.core.mail import send_mail
from django.template import Context, loader
from django_cached_field import CachedDecimalField, CachedFloatField, CachedIntegerField, CachedCharField
import django_snailtracker
import django_snailtracker.models
from beehive.models import BaseModel, SearchableBaseModel
from drops.managers import DropPointTripManager
from drops.constants import (EXCLUSIVITY_CHOICES, PICK_AREA_STATUS_NEW,
PICK_AREA_STATUS_PARTIAL, PICK_AREA_STATUS_CLAIMED,
PICK_AREA_STATUS_DONE, DROP_COLOR_NAMES, MINIMUM_DROP_AMOUNT,
RGB, DROP_MANAGER_RCV_TYPE, DROP_MANAGER_RCV_MUST_SHOW,
DROP_MANAGER_CONTACT_TYPE, DROP_MANAGER_CONTACT_YES,
DROP_MANAGER_FEES_TYPE, DROP_MANAGER_FEES_NO_TIPS,
DROP_MANAGER_NEW_MEMBER_TYPE, DROP_MANAGER_NEW_MEMBER_PLEASE_CONTACT,
DROP_MANAGER_CONTACT_METHOD_TYPE, DROP_MANAGER_CONTACT_METHOD_EMAIL,
DROP_MANAGER_PARKING_TYPE, DROP_MANAGER_PARKING_HOME,
DROP_MANAGER_STORABLES_TYPE, DROP_MANAGER_STORABLES_DRY)
from inventory.models import Stock
from lib.aquameta.addresses.models import Address
from lib.aquameta.helpers.models import cached_property
from lib.aquameta.helpers.util import CurryByArea, phasing
from orders import ORDER_STATUS_PLACED
from palletizing import PALLET_STATUS_DONE
from picking import PICK_STATUS_NEW, PICK_STATUS_DONE, SMALL_PICK, BULK_PICK, \
DRY_CHILLED_PICK_CLIMATE, FROZEN_PICK_CLIMATE, GREENHOUSE_PICK_CLIMATE
from routes.models import Route, RouteTrip, ROUTE_TRIP_SHIPPED, ROUTE_TRIP_VERIFIED
import logging
cache_logger = logging.getLogger('beehive.cache')
deprecation = logging.getLogger('DEPRECATED')
class DropPointStorablesType(BaseModel):
storable_type = models.CharField(max_length=100)
def __unicode__(self):
return self.storable_type
class DropPointContactType(BaseModel):
contact_type = models.CharField(max_length=100)
def __unicode__(self):
return self.contact_type
class DropPointManagerPreferences(BaseModel):
@classmethod
def suggested_defaults(cls):
# This is the suggested defaults for this model if you want to use
# them somewhere.
return {
'driver_callable_phone': '',
'parking_type': DROP_MANAGER_PARKING_HOME,
'other_parking': '',
'members_receive': DROP_MANAGER_RCV_MUST_SHOW,
'hold_time': '',
'contact_need': DROP_MANAGER_CONTACT_YES,
'drop_fees': DROP_MANAGER_FEES_NO_TIPS,
'accept_cod': True,
'fee_amount_and_structure': '',
'publishable': True,
'new_members': DROP_MANAGER_NEW_MEMBER_PLEASE_CONTACT,
'customer_callable_phone': '',
'other_contact': '',
'notes': ''
}
driver_callable_phone = models.CharField(max_length=25, blank=True,
null=True,
verbose_name="Phone number the driver can call to get in touch with you: ")
parking_type = models.IntegerField(choices=DROP_MANAGER_PARKING_TYPE,
verbose_name="What kind of drop location is your drop? ",
null=True)
other_parking = models.CharField(max_length=80, blank=True,
null=True,
verbose_name="")
members_receive = models.IntegerField(choices=DROP_MANAGER_RCV_TYPE,
verbose_name="How do your members receive their items?",
null=True)
hold_time = models.CharField(max_length=25, blank=True,
null=True,
verbose_name = "How long after delivery will you hold orders?")
contact_need = models.IntegerField(choices=DROP_MANAGER_CONTACT_TYPE,
verbose_name="Does the customer need to contact you to pick up their order?",
null=True)
item_store = models.ManyToManyField(DropPointStorablesType,
null=True,
verbose_name="What kind of items can you store?")
drop_fees = models.IntegerField(choices=DROP_MANAGER_FEES_TYPE,
verbose_name="Do you charge any fees?",
null=True)
accept_cod = models.NullBooleanField(
null=True,
verbose_name="Does your drop accept COD orders? ")
fee_amount_and_structure = models.CharField(max_length=140, blank=True,
null=True,
verbose_name="How much, and what is your fee structure?")
publishable = models.NullBooleanField(null=True,
verbose_name="Can we publish your drop on the website?")
new_members = models.IntegerField(choices=DROP_MANAGER_NEW_MEMBER_TYPE,
null=True,
verbose_name="How are new members added to your drop?")
contact_type = models.ManyToManyField(DropPointContactType,
null=True,
verbose_name="How can customers contact you? ")
customer_callable_phone = models.CharField(max_length=25, blank=True,
null=True,
verbose_name="")
other_contact = models.CharField(max_length=80, blank=True,
null=True,
verbose_name="")
notes = models.TextField(blank=True, null=True,
verbose_name="If you'd like to tell us anything else, write it here. ")
class DropPoint(SearchableBaseModel, Address):
"""
DropPoint is imported from the customers table of the website in
002-droppoint.sql some fields are incomplete
"""
searchable_fields = ['name', 'drop_code', 'address_1', 'address_2',
'city', 'state', 'province', 'country',
'postal_code']
routes = models.ManyToManyField("routes.Route",
through="drops.RouteDropAssoc")
route_trips = models.ManyToManyField("routes.RouteTrip",
through="drops.DropPointTrip")
primary_contact = models.ForeignKey('customers.Customer', null=True,
blank=True, related_name='managed_drop_point',
on_delete=models.SET_NULL)
directions = models.TextField(blank=True, null=True)
active = models.BooleanField(default=True)
exclusivity = models.CharField(max_length=10,
choices=EXCLUSIVITY_CHOICES, blank=True, null=True)
_preferences = models.OneToOneField("drops.DropPointManagerPreferences",
null=True)
@classmethod
def ups(cls):
return cls.objects.get(contact_name="UPS")
@classmethod
def dufur_will_call(cls):
return cls.objects.get(contact_name="Dufur Will-Call")
@classmethod
def moro_will_call(cls):
return cls.objects.get(contact_name="Moro Will-Call")
def __unicode__(self):
return "%s: %s" % (self.drop_code, self.name)
def droppointmembership_for_edit(self):
return self.droppointmembership_set.all().order_by('-is_drop_manager', 'customer__user__last_name', 'customer__user__first_name')
def add_member(self, customer):
"""Adds a customer to the droppoint as a non-default point.
Returns the membership
"""
drop_membership = DropPointMembership.objects.filter(
customer=customer, drop_point=self)
if not drop_membership.exists():
drop_membership = DropPointMembership(customer=customer, drop_point=self)
drop_membership.save()
return drop_membership
return drop_membership[0]
def email_drop_manager(self, customer, order):
if self.primary_contact.email and self.primary_contact.email != 'NA':
template = loader.get_template('drops/website/new_member_email.html')
context = Context({'customer': customer, 'order': order, 'drop_point': self})
send_mail(
'A Customer Has Joined Your Drop',
template.render(context),
'info@azurestandard.com',
(self.primary_contact.email,),
fail_silently=False)
@property
def preferences(self):
# We only want preferences to exist if they've been saved by the customer.
#if self._preferences == None:
# self._preferences = DropPointManagerPreferences.objects.create()
# self.save()
return self._preferences
@preferences.setter
def preferences(self, value):
self._preferences = value
@property
def order_frequency_list_for_one_year(self):
one_year_ago = datetime.now() - timedelta(days=180)
dpts = self.droppointtrip_set.filter(route_trip__cutoff__gte=one_year_ago, route_trip__cutoff__lte=datetime.now())
return [drop_trip.orders.count() for drop_trip in dpts.all()]
@property
def last_shipable_drop_point_trip(self):
try:
return self.droppointtrip_set.filter(will_definitely_pick=True).order_by('-route_trip__departure')[0]
except IndexError:
return None
@property
def last_ten_drop_point_trips(self):
return self.droppointtrip_set.filter(route_trip__departure__lte=datetime.now()).order_by('-route_trip__departure')[:10]
@models.permalink
def get_absolute_url(self):
return ('drop_point_detail', [self.id])
@property
def state_or_province(self):
return self.state or self.province
def one_line_address(self):
return "%s %s, %s, %s, %s" % (self.address_1 or "", self.address_2 or "", self.city or "", self.state_or_province or "", self.country or "")
def get_exclusivity_pretty(self):
if self.exclusivity == None:
return "Unknown"
else:
return self.get_exclusivity_display()
def save(self, *args, **kwargs):
for droppointtrip in self.droppointtrip_set.all():
droppointtrip.flag_name_as_stale()
super(DropPoint, self).save(*args, **kwargs)
class Meta:
verbose_name_plural = "Drop Points"
ordering = ('contact_name',)
@property
def drop_code(self):
if self.id:
return "D%s" % self.id
else:
return ""
def route_trips_that_will_stop_here(self):
if 'csw' in settings.ENV:
return self.route_trips.filter(status__lt=ROUTE_TRIP_VERIFIED).order_by('departure')
else:
return self.route_trips.filter(cutoff__gt=datetime.now()).order_by('departure')
@property
def next_pending_trip(self):
try:
rt = self.route_trips.filter(departure__gt=datetime.now()).order_by('departure')[0]
return rt.droppointtrip_set.get(drop_point=self)
except:
return None
@property
def is_ups(self):
return self.name == "UPS"
@property
def is_moro_will_call(self):
return self.name == "Moro Will-Call"
@property
def is_dufur_will_call(self):
return self.name == "Dufur Will-Call"
@property
def name(self):
return self.contact_name
@name.setter
def name(self, value):
self.contact_name = value
deprecation.warning('Use of "DropPoint.name =" is deprecated!')
@phone.getter
def phone(self):
return "fooy"
class DropPointMembership(BaseModel):
"""
DropPointMembership is imported from the customers table of the website in 003-droppointmembership.sql
The import script needs to be checked to make sure ids are correct, could be improved
Hard coded values need to be verified
"""
customer = models.ForeignKey('customers.Customer', on_delete=models.CASCADE)
drop_point = models.ForeignKey(DropPoint, on_delete=models.CASCADE)
is_default_drop = models.BooleanField(default=False)
cutoff_email_notifications = models.BooleanField(default=True)
cutoff_reminder_when_ordered = models.BooleanField(default=False)
manager_notified = models.BooleanField(default=False)
snailtracker_child_of = 'drop_point'
# permissions - TODO - pull these!!
is_approved = models.BooleanField(default=False)
is_drop_manager = models.BooleanField(default=False)
can_email = models.BooleanField(default=True)
can_call = models.BooleanField(default=True)
allow_chilled = models.BooleanField(default=True)
allow_frozen = models.BooleanField(default=True)
allow_cod = models.BooleanField(default=False)
must_meet_truck = models.BooleanField(default=False)
class Meta:
ordering = ('customer',)
def __unicode__(self):
return self.drop_point.drop_code
class SentCutoffNotification(BaseModel):
drop_point_trip = models.ForeignKey('DropPointTrip')
customer = models.ForeignKey('customers.Customer')
class RouteDropAssoc(BaseModel):
"""
DropPoint is imported from the customers table of the website joined with routes_route and drops_droppoint in 006-routedropassoc.sql
distance and sequence are not in the import
"""
route = models.ForeignKey("routes.Route", on_delete=models.CASCADE)
drop_point = models.ForeignKey(DropPoint, on_delete=models.CASCADE)
applies_shipping_fee = models.BooleanField(default=True)
distance = models.IntegerField(default=0)
sequence = models.IntegerField(null=True, blank=True)
class Meta:
unique_together = (("route", "drop_point",),)
ordering = ("sequence","distance","drop_point")
def __unicode__(self):
return "RouteDropAssociation: %s -> %s" % (self.route, self.drop_point)
class PickingInProgressError(Exception):
pass
class PickingDoneError(Exception):
pass
class DropPointTrip(BaseModel):
drop_point = models.ForeignKey(DropPoint, on_delete=models.PROTECT)
route_trip = models.ForeignKey(RouteTrip, on_delete=models.PROTECT)
color_number = models.IntegerField(null=True, blank=True)
will_definitely_pick = models.NullBooleanField(default=None, null=True, blank=True)
distance = CachedIntegerField(null=True, blank=True)
name = CachedCharField(max_length=255, null=True, blank=True)
ordered_amount = CachedDecimalField(max_digits=12, decimal_places=2, null=True, blank=True)
ordered_volume_cubic_ft = CachedFloatField(null=True, blank=True)
ordered_weight = CachedFloatField(null=True, blank=True)
picked_weight = CachedFloatField(null=True, blank=True)
picked_volume = CachedFloatField(null=True, blank=True)
sequence = CachedIntegerField(null=True, blank=True)
snailtracker_child_of = 'drop_point'
#TODO cache drop address here!
objects = DropPointTripManager()
class Meta:
ordering = ["route_trip__departure", "cached_sequence"]
unique_together = (("route_trip", "cached_sequence",),)
def __unicode__(self):
return "DropPointTrip: %s[%s]" % (self.route_trip, self.drop_point)
def save(self, *args, **kwargs):
if self.cached_sequence is None:
self.cached_sequence = self.calculate_sequence()
self.cached_distance = self.calculate_distance()
self.cached_name = self.calculate_name()
super(DropPointTrip, self).save(*args, **kwargs)
def update_cached_ordered_attrs(self):
cache_logger.debug('update_cached_ordered_attrs on dpt %s' % (self,))
self.cached_ordered_amount = self.calculate_ordered_amount()
self.cached_ordered_weight = self.calculate_ordered_weight()
self.cached_ordered_volume_cubic_ft = self.calculate_ordered_volume_cubic_ft()
def update_aggregate_references(self):
self.route_trip.update_cached_attrs()
self.route_trip.save()
if self.pick_set is not None:
self.pick_set.update_cached_attrs()
self.pick_set.save()
def by_area(self, pick_climate=None, bulk=None):
return CurryByArea(self, pick_climate, bulk)
@property
def route_drop_assoc(self):
return self.route_trip.route.routedropassoc_set.get(drop_point=self.drop_point)
@property
def shipping_fee_percent(self):
return 8.5
@property
def frozen(self):
return self.by_area(pick_climate=FROZEN_PICK_CLIMATE)
@property
def greenhouse(self):
return self.by_area(pick_climate=GREENHOUSE_PICK_CLIMATE)
@property
def dry_chilled(self):
return self.by_area(pick_climate=DRY_CHILLED_PICK_CLIMATE)
@property
def applies_shipping_fee(self):
return self.route_drop_assoc.applies_shipping_fee
@property
def has_shipping_fees(self):
return self.route_trip.route.has_shipping_fees and self.applies_shipping_fee
@property
def order_shipments_shipping(self):
return self.ordershipment_set.filter(order__order_status__gte=ORDER_STATUS_PLACED)
@property
def orders(self):
Order = models.get_model('orders', 'Order')
return Order.objects.filter(ordershipment__in=self.order_shipments_shipping)
def delay_orders_until_next_possible_route_trip(self):
for os in list(self.order_shipments_shipping):
os.order.push_to_next_possible_ship_date()
@cached_property
def pick_set(self):
PickSet = models.get_model('pick_set', 'PickSet')
if self.sequence is None:
return None
try:
return self.route_trip.pick_sets.get(drop_point_sequence_start__lte=self.sequence,
drop_point_sequence_end__gte=self.sequence)
except PickSet.DoesNotExist:
return None
def order_pieces_by_area(self, pick_climate=None, bulk=None):
opieces = self.orderpiece_set.all()
if pick_climate:
opieces = opieces.in_pick_climate(pick_climate)
if bulk:
opieces = opieces.in_small_or_bulk(bulk)
return opieces
def order_pieces_frozen(self):
return self.order_pieces_by_area(pick_climate=FROZEN_PICK_CLIMATE)
def order_pieces_dry(self):
return self.order_pieces_by_area(pick_climate=DRY_CHILLED_PICK_CLIMATE)
def order_pieces_to_be_picked_by_area(self, pick_climate=None, bulk=None):
return self.order_pieces_by_area(pick_climate=pick_climate, bulk=bulk).filter(stock=None)
def stock_set_by_area(self, pick_climate=None, bulk=None):
return Stock.objects.filter(pk__in=self.order_pieces_by_area(pick_climate=pick_climate, bulk=bulk).values_list('stock_id', flat=True))
def palletizing_done_by_area(self, pick_climate=None, bulk=None):
return(
not self.needs_picking_by_area(pick_climate, bulk)
and self.piles_to_palletize_by_area(pick_climate, bulk) == self.piles_palletized_by_area(pick_climate, bulk)
and self.boxes_to_palletize_by_area(pick_climate, bulk) == self.boxes_palletized_by_area(pick_climate, bulk)
#:MC: wtf? using .exists() on the next line causes timeouts!
and self.pallets_by_area(pick_climate, bulk).filter(status__lt=PALLET_STATUS_DONE).count() == 0)
def pallets_by_area(self, pick_climate=None, bulk=None):
Pallet = models.get_model('palletizing', 'Pallet')
stock = self.stock_set_by_area(pick_climate, bulk)
pallets = Pallet.objects.filter(stock__in=stock)
return pallets
def piles_by_area(self, pick_climate=None, bulk=None):
bulk = BULK_PICK #:MC: piles are only bulk
return self.pick_claims_by_area(pick_climate=pick_climate, bulk=bulk).filter(status=PICK_STATUS_DONE)
def piles_to_palletize_by_area(self, pick_climate=None, bulk=None):
return self.piles_by_area(pick_climate, bulk).count()
def piles_palletized_by_area(self, pick_climate=None, bulk=None):
bulk = BULK_PICK #:MC: piles are only bulk
return len(set(self.stock_set_by_area(pick_climate, bulk).exclude(pallet=None).values_list('pick_claim', flat=True)))
def boxes_by_area(self, pick_climate=None, bulk=None):
Box = models.get_model('picking', 'Box')
bulk = SMALL_PICK #:MC: boxes are only small
return reduce(
lambda boxes, claim: boxes | claim.orderpick.box_set.all(),
self.pick_claims_by_area(pick_climate=pick_climate, bulk=bulk).filter(status=PICK_STATUS_DONE),
Box.objects.none())
def boxes_to_palletize_by_area(self, pick_climate=None, bulk=None):
return self.boxes_by_area(pick_climate, bulk).count()
def boxes_palletized_by_area(self, pick_climate=None, bulk=None):
bulk = SMALL_PICK #:MC: boxes are only small
return len(set(self.stock_set_by_area(pick_climate, bulk).exclude(pallet=None).values_list('box', flat=True)))
def orders_by_area(self, pick_climate=None, bulk=None):
Order = models.get_model('orders', 'Order')
return [order.by_area(pick_climate, bulk)
for order in Order.objects.filter(pk__in=self.order_shipments_shipping.values_list("order", flat=True))
if order.in_area(pick_climate, bulk)]
def needs_picking_by_area(self, pick_climate=None, bulk=None):
status = self.picking_status_by_area(pick_climate, bulk)
return status in (PICK_AREA_STATUS_NEW, PICK_AREA_STATUS_PARTIAL)
def pick_claim_for_by_area(self, user, pick_climate=None, bulk=None):
bulk = BULK_PICK
try:
return(self.pickclaim_set.filter(
small_or_bulk=bulk, pick_climate=pick_climate)
.exclude(status=PICK_STATUS_DONE)
.get(picker=user))
except:
return None
def picking_status_by_area(self, pick_climate=None, bulk=None):
pick_claims = self.pick_claims_by_area(pick_climate=pick_climate, bulk=bulk).exclude(status=PICK_STATUS_NEW)
if not pick_claims.exists():
return PICK_AREA_STATUS_NEW
elif not pick_claims.exclude(status=PICK_STATUS_DONE).exists():
needed = (self.order_pieces_by_area(
pick_climate=pick_climate, bulk=bulk)
.filter(stock=None, shorted=False,)) #piece__out_of_stock=False
if not needed.exists():
return PICK_AREA_STATUS_DONE
else:
return PICK_AREA_STATUS_PARTIAL
else: # unfinished pick claims exist
unclaimed = (self.order_pieces_by_area(
pick_climate=pick_climate, bulk=bulk)
.filter(stock=None, #piece__out_of_stock=False,
shorted=False, pick_claim=None))
if unclaimed.exists():
return PICK_AREA_STATUS_PARTIAL
else:
return PICK_AREA_STATUS_CLAIMED
# :MC: this code was too slow.
# ordered = self.total_piece_count_by_area(pick_climate=pick_climate, bulk=bulk)
# picking = self.order_pieces_by_area(pick_climate=pick_climate, bulk=bulk)
# inprogress = picking.filter(
# pick_claim__status=PICK_STATUS_CLAIMED
# ).count()
# picked = picking.exclude(stock=None).count() # + picking.filter(shorted=True, stock=None).count()
# if picked + inprogress == 0:
# return PICK_AREA_STATUS_NEW
# elif( not pick_claims.exclude(status=PICK_STATUS_DONE).exists()
# and picked >= ordered):
# return PICK_AREA_STATUS_DONE
# elif inprogress >= (ordered - picked):
# return PICK_AREA_STATUS_CLAIMED
# else:
# return PICK_AREA_STATUS_PARTIAL
def pick_claims_by_area(self, pick_climate=None, bulk=None):
pclaims = self.pickclaim_set.all()
if pick_climate:
pclaims = pclaims.filter(pick_climate=pick_climate)
if bulk:
pclaims = pclaims.filter(small_or_bulk=bulk)
return pclaims
def tentative_pick_claim_by_area(self, pick_climate, bulk):
bulk = bulk or BULK_PICK
status = self.picking_status_by_area(pick_climate, bulk)
if status == PICK_AREA_STATUS_CLAIMED:
raise PickingInProgressError
elif status == PICK_AREA_STATUS_DONE:
raise PickingDoneError
else:
# TODO martin_c: this was a hack-in to fix a picking problem. We should figure out how this failed and fix it.
if self.pickclaim_set.filter(pick_climate=pick_climate, small_or_bulk=bulk, picker__isnull=True).exists():
return self.pickclaim_set.filter(pick_climate=pick_climate, small_or_bulk=bulk, picker__isnull=True)[0]
else:
return self.pickclaim_set.get_or_create(pick_climate=pick_climate, small_or_bulk=bulk, picker__isnull=True)[0]
def pieces_by_area(self, pick_climate=None, bulk=None):
Piece = models.get_model('warehouse_pieces', 'Piece')
return Piece.objects.filter(pk__in=self.order_pieces_by_area(pick_climate, bulk).values_list("piece_id", flat=True)).distinct()
def total_piece_count_by_area(self, pick_climate=None, bulk=None):
return (self.order_pieces_by_area(pick_climate=pick_climate, bulk=bulk)
.count())
def ordered_weight_by_area(self, pick_climate=None, bulk=None):
return sum([o.ordered_weight for o in self.orders_by_area(pick_climate, bulk)])
def picked_weight_by_area(self, pick_climate=None, bulk=None):
return sum([st.weight
for st in self.stock_set_by_area(pick_climate, bulk).select_related("piece")])
def ordered_volume_cubic_ft_by_area(self, pick_climate=None, bulk=None):
return sum([o.ordered_volume_cubic_ft for o in self.orders_by_area(pick_climate, bulk)])
def picked_volume_cubic_ft_by_area(self, pick_climate=None, bulk=None):
return sum([st.piece.volume_cubic_ft
for st in self.stock_set_by_area(pick_climate, bulk).select_related("piece")])
def shippables_count_by_area(self, pick_climate=None, bulk=None):
count = 0
for claim in self.pick_claims_by_area(pick_climate, bulk).exclude(orderpick=None):
count += claim.orderpick.box_set.count()
count += self.stock_set_by_area(pick_climate, BULK_PICK).count()
return count
@property
def has_small_dry_and_chilled(self):
return self.order_pieces_by_area(DRY_CHILLED_PICK_CLIMATE, SMALL_PICK).exists()
@property
def has_bulk_dry_and_chilled(self):
return self.order_pieces_by_area(DRY_CHILLED_PICK_CLIMATE, BULK_PICK).exists()
@property
def has_small_frozen(self):
return self.order_pieces_by_area(FROZEN_PICK_CLIMATE, SMALL_PICK).exists()
@property
def has_bulk_frozen(self):
return self.order_pieces_by_area(FROZEN_PICK_CLIMATE, BULK_PICK).exists()
@property
def has_small_greenhouse(self):
return self.order_pieces_by_area(GREENHOUSE_PICK_CLIMATE, SMALL_PICK).exists()
@property
def truck_number(self):
return self.pick_set.truck_number
@property
def drop_sticker_color(self):
return "drop_sticker_color_%d" % self.color_number
@property
def drop_sticker_color_name(self):
if self.color_number is not None:
return DROP_COLOR_NAMES[self.color_number]
else:
return ""
@property
def drop_sticker_color_rgb(self):
return RGB[self.color_number]
@property
def drop_code(self):
if self.drop_point_id:
return "D%s" % self.drop_point_id
else:
return ""
@property
def picked_amount(self):
return sum([o.order.picked_price for o in self.order_shipments_shipping])
@property
def invoiced_total(self):
return sum([o.order.invoiced_total for o in self.order_shipments_shipping])
def calculate_name(self):
return self.drop_point.name
def calculate_sequence(self):
return self.route_drop_assoc.sequence
def calculate_distance(self):
return self.route_drop_assoc.distance
def calculate_ordered_amount(self):
return sum([o.ordered_price for o in self.orders])
def calculate_ordered_volume_cubic_ft(self):
return sum([o.ordered_volume_cubic_ft for o in self.orders])
@property
def weight_to_display(self):
return self.calculate_weight_to_display()
@phasing("route_trip.status")
def calculate_weight_to_display(self):
return self.ordered_weight
@calculate_weight_to_display.phase_after(ROUTE_TRIP_SHIPPED)
def calculate_weight_to_display(self):
return self.picked_weight
@property
def volume_to_display(self):
return self.calculate_volume_to_display()
@phasing("route_trip.status")
def calculate_volume_to_display(self):
return self.ordered_volume_cubic_ft
@calculate_volume_to_display.phase_after(ROUTE_TRIP_SHIPPED)
def calculate_volume_to_display(self):
return self.picked_volume
@property
def amount_to_display(self):
return self.calculate_amount_to_display()
@phasing("route_trip.status")
def calculate_amount_to_display(self):
return self.ordered_amount
@calculate_amount_to_display.phase_after(ROUTE_TRIP_SHIPPED)
def calculate_amount_to_display(self):
return self.picked_amount
def calculate_ordered_weight(self):
return sum([o.ordered_weight for o in self.orders.all()])
def calculate_picked_weight(self):
return sum([o.picked_weight for o in self.orders.all()])
def calculate_picked_volume(self):
return sum([o.picked_volume_cubic_ft for o in self.orders.all()])
@property
def meets_drop_minimums(self):
return self.ordered_amount >= MINIMUM_DROP_AMOUNT
@property
def will_pick(self):
if self.will_definitely_pick is not None:
return self.will_definitely_pick
elif self.route_trip.route in (Route.ups(), Route.moro_will_call(), Route.dufur_will_call()):
return True
else:
return self.meets_drop_minimums
def set_will_definitely_pick_off(self):
self.will_definitely_pick = False
self.save()
self.update_aggregate_references()
def set_will_definitely_pick_on(self):
self.will_definitely_pick = True
self.save()
self.update_aggregate_references()
def save_will_pick(self):
if self.will_definitely_pick != self.will_pick:
self.will_definitely_pick = self.will_pick
self.save()
# self.update_aggregate_references() #:MC: this is killing the server
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment