forked from awesto/django-shop
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdelivery.py
130 lines (111 loc) · 4.58 KB
/
delivery.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from six import with_metaclass
from django.core import checks
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from shop import deferred
from shop.models.order import BaseOrder, BaseOrderItem, OrderItemModel
from shop.modifiers.pool import cart_modifiers_pool
@python_2_unicode_compatible
class BaseDelivery(with_metaclass(deferred.ForeignKeyBuilder, models.Model)):
"""
Shipping provider to keep track on each delivery.
"""
order = deferred.ForeignKey(BaseOrder)
shipping_id = models.CharField(
_("Shipping ID"),
max_length=255,
null=True,
blank=True,
help_text=_("The transaction processor's reference"),
)
fulfilled_at = models.DateTimeField(
_("Fulfilled at"),
null=True,
blank=True,
help_text=_("Timestamp of delivery fulfillment"),
)
shipped_at = models.DateTimeField(
_("Shipped at"),
null=True,
blank=True,
help_text=_("Timestamp of delivery shipment"),
)
shipping_method = models.CharField(
_("Shipping method"),
max_length=50,
help_text=_("The shipping backend used to deliver items of this order"),
)
class Meta:
abstract = True
unique_together = ['shipping_method', 'shipping_id']
get_latest_by = 'shipped_at'
def __str__(self):
return _("Delivery ID: {}").format(self.id)
@classmethod
def check(cls, **kwargs):
errors = super(BaseDelivery, cls).check(**kwargs)
for field in OrderItemModel._meta.fields:
if field.attname == 'canceled' and field.get_internal_type() == 'BooleanField':
break
else:
msg = "Class `{}` must implement a `BooleanField` named `canceled`, if used in combination with a Delivery model."
errors.append(checks.Error(msg.format(OrderItemModel.__name__)))
return errors
def clean(self):
if self.order._fsm_requested_transition == ('status', 'ship_goods') and not self.shipped_at:
shipping_modifier = cart_modifiers_pool.get_active_shipping_modifier(self.shipping_method)
shipping_modifier.ship_the_goods(self)
def get_number(self):
"""
Hook to get the delivery number.
A class inheriting from Order may transform this into a string which is better readable.
"""
if self.order.allow_partial_delivery:
for part, delivery in enumerate(self.order.delivery_set.all(), 1):
if delivery.pk == self.pk:
return "{} / {}".format(self.order.get_number(), part)
return self.order.get_number()
DeliveryModel = deferred.MaterializedModel(BaseDelivery)
class BaseDeliveryItem(with_metaclass(deferred.ForeignKeyBuilder, models.Model)):
"""
Abstract base class to keep track on the delivered quantity for each ordered item. Since the
quantity can be any numerical value, it has to be defined by the class implementing this model.
"""
delivery = deferred.ForeignKey(
BaseDelivery,
verbose_name=_("Delivery"),
related_name='items',
help_text=_("Refer to the shipping provider used to ship this item"),
)
item = deferred.ForeignKey(
BaseOrderItem,
related_name='deliver_item',
verbose_name=_("Ordered item"),
)
class Meta:
abstract = True
verbose_name = _("Deliver item")
verbose_name_plural = _("Deliver items")
@classmethod
def check(cls, **kwargs):
errors = super(BaseDeliveryItem, cls).check(**kwargs)
for order_field in OrderItemModel._meta.fields:
if order_field.attname == 'quantity':
break
else:
msg = "Class `{}` must implement a field named `quantity`."
errors.append(checks.Error(msg.format(OrderItemModel.__name__)))
for deliver_field in OrderItemModel._meta.fields:
if deliver_field.attname == 'quantity':
break
else:
msg = "Class `{}` must implement a field named `quantity`."
errors.append(checks.Error(msg.format(cls.__name__)))
if order_field.get_internal_type() != deliver_field.get_internal_type():
msg = "Field `{}.quantity` must be of one same type `{}.quantity`."
errors.append(checks.Error(msg.format(cls.__name__, OrderItemModel.__name__)))
return errors
DeliveryItemModel = deferred.MaterializedModel(BaseDeliveryItem)