Skip to content

Commit

Permalink
Fix/update order detail logic (#53)
Browse files Browse the repository at this point in the history
* fix: abstract class arguments

* fix: move create_order_details function

* fix: function to class

* fix: stock checker mvoe

* fix: stop deploy

* fix: deploy workflow
  • Loading branch information
aohus authored Aug 16, 2024
1 parent 325e075 commit c1bab4c
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 77 deletions.
54 changes: 27 additions & 27 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,32 +41,32 @@ jobs:
tags: aohus/virtu-mall:latest
no-cache: true

- name: Copy docker-compose.yml to Server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.AWS_EC2_HOST }}
username: ${{ secrets.AWS_EC2_USERNAME }}
key: ${{ secrets.AWS_EC2_SSH_KEY }}
source: "docker-compose.yml"
target: "/home/ubuntu"
# - name: Copy docker-compose.yml to Server
# uses: appleboy/scp-action@master
# with:
# host: ${{ secrets.AWS_EC2_HOST }}
# username: ${{ secrets.AWS_EC2_USERNAME }}
# key: ${{ secrets.AWS_EC2_SSH_KEY }}
# source: "docker-compose.yml"
# target: "/home/ubuntu"

- name: Copy nginx.conf to Server
uses: appleboy/scp-action@master
with:
host: ${{ secrets.AWS_EC2_HOST }}
username: ${{ secrets.AWS_EC2_USERNAME }}
key: ${{ secrets.AWS_EC2_SSH_KEY }}
source: "configs/nginx.conf"
target: "/home/ubuntu"
# - name: Copy nginx.conf to Server
# uses: appleboy/scp-action@master
# with:
# host: ${{ secrets.AWS_EC2_HOST }}
# username: ${{ secrets.AWS_EC2_USERNAME }}
# key: ${{ secrets.AWS_EC2_SSH_KEY }}
# source: "configs/nginx.conf"
# target: "/home/ubuntu"

- name: Deploy to AWS EC2
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.AWS_EC2_HOST }}
username: ${{ secrets.AWS_EC2_USERNAME }}
key: ${{ secrets.AWS_EC2_SSH_KEY }}
script: |
docker-compose down
docker rmi $(docker images -qa)
docker pull aohus/virtu-mall
docker-compose up -d
# - name: Deploy to AWS EC2
# uses: appleboy/ssh-action@master
# with:
# host: ${{ secrets.AWS_EC2_HOST }}
# username: ${{ secrets.AWS_EC2_USERNAME }}
# key: ${{ secrets.AWS_EC2_SSH_KEY }}
# script: |
# docker-compose down
# docker rmi $(docker images -qa)
# docker pull aohus/virtu-mall
# docker-compose up -d
15 changes: 2 additions & 13 deletions src/apps/payment/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from apps.payment.models.order import Order
from apps.payment.models.order import OrderDetail
from apps.payment.models.wallet import Wallet
from apps.payment.services import pay
from apps.payment.services import pay_service


class WalletSerializer(serializers.ModelSerializer):
Expand All @@ -30,16 +30,6 @@ class Meta:
fields = ["user", "total_price", "shipping_address", "order_detail"]
read_only_fields = ("user", "total_price", "shipping_address")

def create_order_details(
self, order: Order, order_detail_data: dict[str, Any]
) -> None:
OrderDetail.objects.bulk_create(
[
OrderDetail(order=order, **detail_data)
for detail_data in order_detail_data
]
)

def create(self, validated_data: Dict[str, Any]) -> Order:
user = self.context["request"].user
if not user.is_buyer:
Expand All @@ -51,10 +41,9 @@ def create(self, validated_data: Dict[str, Any]) -> Order:
validated_data["total_price"] = sum(
[order_detail["total_price"] for order_detail in order_detail_data]
)
wallet = pay(validated_data, order_detail_data)
wallet = pay_service(validated_data, order_detail_data)

order = super().create(validated_data)
self.create_order_details(order, order_detail_data)
wallet.order = order
wallet.save()
return order
Expand Down
106 changes: 71 additions & 35 deletions src/apps/payment/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,47 +12,83 @@
from apps.payment.models.wallet import Wallet


def check_product_stock(order_detail_data: dict[str, Any]) -> None:
for detail_data in order_detail_data:
product = detail_data["product"]
if product.deleted_at is not None:
raise ValidationError("update_product_stock failed: invalid product")

stock = (
product.stock
- OrderDetail.objects.filter(product=product, deleted_at=None).aggregate(
stock=Coalesce(Sum("quantity"), 0)
)["stock"]
class StockChecker:
def check_stock(self, order_detail_data: dict[str, Any]) -> None:
for detail_data in order_detail_data:
product = detail_data["product"]
if product.deleted_at is not None:
raise ValidationError("update_product_stock failed: invalid product")

stock = (
product.stock
- OrderDetail.objects.filter(
product=product, deleted_at=None
).aggregate(stock=Coalesce(Sum("quantity"), 0))["stock"]
)
if detail_data["quantity"] > stock:
raise ValidationError("update_product_stock failed")


class WalletManager:
def update_transaction(self, order: dict[str, Any]) -> None:
balance = Wallet.get_balance(user=order["user"])
if balance < order["total_price"]:
raise ValidationError("update_wallet_transaction failed")

return Wallet.objects.create(
user=order["user"],
transaction_type=Wallet.TransactionType.WITHDRAWAL,
amount=order["total_price"],
mileage=order["total_price"] * 0.03,
)

def delete_transaction(self, order: Order) -> None:
Wallet.objects.filter(order=order).update(deleted_at=order.deleted_at)


class OrderDetailManager:
def __init__(self, stock_checker: StockChecker):
self.stock_checker = stock_checker

def create(self, order: dict[str, Any], order_detail_data: dict[str, Any]) -> None:
self.stock_checker.check_stock(order_detail_data)
OrderDetail.objects.bulk_create(
[
OrderDetail(order=order, **detail_data)
for detail_data in order_detail_data
]
)
if detail_data["quantity"] > stock:
raise ValidationError("update_product_stock failed")

def delete(self, order: Order) -> None:
OrderDetail.objects.filter(order=order).update(deleted_at=order.deleted_at)

def update_wallet_transaction(order: dict[str, Any]) -> None:
balance = Wallet.get_balance(user=order["user"])
if balance < order["total_price"]:
raise ValidationError("update_wallet_transaction failed")

return Wallet.objects.create(
user=order["user"],
transaction_type=Wallet.TransactionType.WITHDRAWAL,
amount=order["total_price"],
mileage=order["total_price"] * 0.03,
)
class PayService:
def __init__(
self,
order_detail_manager: OrderDetailManager,
wallet_manager: WalletManager,
):
self.order_detail_manager = order_detail_manager
self.wallet_manager = wallet_manager

@transaction.atomic
def pay(self, order: Dict[str, Any], order_detail_data: dict[str, Any]) -> None:
self.order_detail_manager.create(order, order_detail_data)
self.wallet_manager.update_transaction(order)

@transaction.atomic
def pay(order: Dict[str, Any], order_detail_data: dict[str, Any]) -> None:
check_product_stock(order_detail_data)
return update_wallet_transaction(order)
@transaction.atomic
def rollback_pay(self, order: Order) -> None:
now = datetime.utcnow()
order.status = Order.Status.CANCELED
order.deleted_at = now
order.save()

self.wallet_manager.delete_transaction(order)
self.order_detail_manager.delete(order)

@transaction.atomic
def rollback_pay(order: Order) -> None:
now = datetime.utcnow()
order.status = Order.Status.CANCELED
order.deleted_at = now
order.save()

Wallet.objects.filter(order=order).update(deleted_at=now)
OrderDetail.objects.filter(order=order).update(deleted_at=now)
stock_checker = StockChecker()
order_detail_manager = OrderDetailManager(stock_checker)
wallet_manager = WalletManager()
pay_service = PayService(wallet_manager, order_detail_manager)
4 changes: 2 additions & 2 deletions src/apps/payment/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
from apps.payment.serializers import OrderDetailSerializer
from apps.payment.serializers import OrderSerializer
from apps.payment.serializers import WalletSerializer
from apps.payment.services import rollback_pay
from apps.payment.services import pay_service
from apps.product.models import Product
from utils.permissions import IsAdminOrOwner
from utils.permissions import IsStore
Expand Down Expand Up @@ -44,7 +44,7 @@ def perform_destroy(self, instance: Order) -> None:
f"delete order failed: order status is '{instance.status}'"
)

rollback_pay(instance)
pay_service.rollback_pay(instance)
return Response(status=status.HTTP_204_NO_CONTENT)


Expand Down

0 comments on commit c1bab4c

Please sign in to comment.