Order customization¶
This page documents different order customization methods.
Custom statuses¶
Order statuses are defined using django’s django.db.models.TextChoices()
enum class with
the default set to salesman.orders.status.OrderStatus
class.
You can change it by providing a dotted path in SALESMAN_ORDER_STATUS
setting
that points to a class extending salesman.orders.status.BaseOrderStatus
class.
class OrderStatus(BaseOrderStatus):
"""
Default order choices enum. Can be overriden by providing a
dotted path to a class in ``SALESMAN_ORDER_STATUS`` setting.
Required choices are NEW, CREATED, COMPLETED and REFUNDED.
"""
NEW = 'NEW', _("New") # Order with reference created, items are in the basket.
CREATED = 'CREATED', _("Created") # Created with items and pending payment.
HOLD = 'HOLD', _("Hold") # Stock reduced but still awaiting payment.
FAILED = 'FAILED', _("Failed") # Payment failed, retry is available.
CANCELLED = 'CANCELLED', _("Cancelled") # Cancelled by seller, stock increased.
PROCESSING = 'PROCESSING', _("Processing") # Payment confirmed, processing order.
SHIPPED = 'SHIPPED', _("Shipped") # Shipped to customer.
COMPLETED = 'COMPLETED', _("Completed") # Completed and received by customer.
REFUNDED = 'REFUNDED', _("Refunded") # Fully refunded by seller.
@classmethod
def get_payable(cls) -> list:
"""
Returns default payable statuses.
"""
return [cls.CREATED, cls.HOLD, cls.FAILED]
@classmethod
def get_transitions(cls) -> dict:
"""
Returns default status transitions.
"""
return {
'NEW': [cls.CREATED],
'CREATED': [cls.HOLD, cls.FAILED, cls.CANCELLED, cls.PROCESSING],
'HOLD': [cls.FAILED, cls.CANCELLED, cls.PROCESSING],
'FAILED': [cls.CANCELLED, cls.PROCESSING],
'CANCELLED': [],
'PROCESSING': [cls.SHIPPED, cls.COMPLETED, cls.REFUNDED],
'SHIPPED': [cls.COMPLETED, cls.REFUNDED],
'COMPLETED': [cls.REFUNDED],
'REFUNDED': [],
}
As seen in the default status class above, you must define statuses with names and values NEW
,
CREATED
, COMPLETED
and REFUNDED
while others are customizable to your needs.
You can also optionally override salesman.orders.status.BaseOrderStatus.get_payable()
method that
returns a list of statuses from which an order is eligible for payment, and
salesman.orders.status.BaseOrderStatus.get_transitions()
method which limits status transitions
for an order.
Validate status transitions¶
You might want to validate certain status transitions for your orders. This is possible by
overriding the salesman.orders.status.BaseOrderStatus.validate_transition()
method.
Tip
Eg. you could enforce a check that the order has been fully paid for before it can be marked as Completed, or even return and force a different status entirely.
A default status transition validation is provided with salesman.orders.status.BaseOrderStatus
class which checks that transition is available for the current order status:
@classmethod
def validate_transition(cls, status: str, order: Order) -> str:
"""
Validate a given status transition for the order.
By default check status is defined in transitions.
Args:
status (str): New status
order (Order): Order instance
Raises:
ValidationError: If transition not valid
Returns:
str: Validated status
"""
transitions = cls.get_transitions().get(order.status, [status])
transitions.append(order.status)
if status not in transitions:
current, new = cls[order.status].label, cls[status].label
msg = _(f"Can't change order with status '{current}' to '{new}'.")
raise ValidationError(msg)
return status
Note
To include base validation be sure to call super()
when overriding validation.
Custom reference generator¶
Order reference is generated when a new order get’s created and represents a unique identifier
for that order. By default reference is generated in a {year}-{5-digit-increment}
format using
salesman.orders.utils.generate_ref()
function.
You can change it by providing a dotted path in SALESMAN_ORDER_REFERENCE_GENERATOR
setting that
points to a function that returns a unique reference string.
def generate_ref(request: HttpRequest) -> str:
"""
Default order reference generator function. Can be overriden by providing a
dotted path to a function in ``SALESMAN_ORDER_REFERENCE_GENERATOR`` setting.
Default format is ``{year}-{5-digit-increment}`` (eg. `2020-00001`).
Args:
request (HttpRequest): Django request
Returns:
str: New order reference
"""
year = timezone.now().year
last = Order.objects.filter(date_created__year=year, ref__isnull=False).first()
increment = int(last.ref.split('-')[1]) + 1 if last and last.ref else 1
return f'{year}-{increment:05d}'
Your custom function should accept Django’s request
object as a parameter.
Custom order serializer¶
You can override order serializer by providing a dotted path in SALESMAN_ORDER_SERIALIZER
setting.
Optionally you can configure a separate order “summary” serializer with SALESMAN_ORDER_SUMMARY_SERIALIZER
setting, otherwise the same order serializer is used. Default serializer for is set
to salesman.orders.serializers.OrderSerializer
.
You can also override the OrderSerializer.Meta.prefetched_fields
property to optimize for any added
relations to your custom serializer.