refactor(api): update shipment review logic to update existing reviews
Refactor `ShipmentReviewService` to update the existing pending review instead of creating a new `ShipmentReview` record during the approval and rejection processes. This ensures that the review lifecycle correctly finalizes the current pending revision. - Implement `getPendingReview` to retrieve the active pending review - Update `approve` to modify the existing review record - Update `reject` to modify the existing review record - Add documentation clarifying when new revisions are created versus when existing ones are finalized
This commit is contained in:
parent
69800a2b1e
commit
7faa778e22
@ -1 +1 @@
|
||||
{"version":2,"defects":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":5,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":7,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":8,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":8,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":8,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_invalid_discount_code":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_expired_discount_code":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_code_below_minimum_order_amount":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_calculates_fixed_discount_correctly":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_redirects_to_gateway_for_payment":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_validates_minimum_deposit_amount":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_prevents_payment_for_frozen_wallet":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_checks_transaction_status":8},"times":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":0.057,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":0.057,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":0.012,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0.013,"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.055,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":0.1,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":0.059,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_invalid_discount_code":0.048,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_expired_discount_code":0.047,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_code_below_minimum_order_amount":0.059,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_calculates_fixed_discount_correctly":0.047,"Tests\\Feature\\Api\\PaymentControllerTest::it_redirects_to_gateway_for_payment":0.081,"Tests\\Feature\\Api\\PaymentControllerTest::it_validates_minimum_deposit_amount":0.054,"Tests\\Feature\\Api\\PaymentControllerTest::it_prevents_payment_for_frozen_wallet":0.046,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":0.065,"Tests\\Feature\\Api\\PaymentControllerTest::it_checks_transaction_status":0.054,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":0.003,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":0.008,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":0.005,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":0.006,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":0.012,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":0.004,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":0.002,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":0.005}}
|
||||
{"version":2,"defects":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":5,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":7,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":8,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":8,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":8,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":8,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":8,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_invalid_discount_code":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_expired_discount_code":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_code_below_minimum_order_amount":8,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_calculates_fixed_discount_correctly":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_redirects_to_gateway_for_payment":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_validates_minimum_deposit_amount":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_prevents_payment_for_frozen_wallet":8,"Tests\\Feature\\Api\\PaymentControllerTest::it_checks_transaction_status":8},"times":{"Tests\\Feature\\Api\\PricingControllerTest::it_can_calculate_pricing_with_valid_data":0.33,"Tests\\Feature\\Api\\PricingControllerTest::it_returns_validation_errors_for_invalid_data":0.063,"Tests\\Feature\\Services\\PriceCalculatorServiceTest::it_calculates_price_correctly_for_standard_package":0.044,"Tests\\Unit\\ExampleTest::test_that_true_is_true":0.008,"Tests\\Feature\\ExampleTest::test_the_application_returns_a_successful_response":0.373,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_returns_active_discount_codes":0.429,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_validates_discount_code_successfully":0.058,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_invalid_discount_code":0.044,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_expired_discount_code":0.044,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_rejects_code_below_minimum_order_amount":0.044,"Tests\\Feature\\Api\\DiscountCodeControllerTest::it_calculates_fixed_discount_correctly":0.043,"Tests\\Feature\\Api\\PaymentControllerTest::it_redirects_to_gateway_for_payment":0.517,"Tests\\Feature\\Api\\PaymentControllerTest::it_validates_minimum_deposit_amount":0.051,"Tests\\Feature\\Api\\PaymentControllerTest::it_prevents_payment_for_frozen_wallet":0.046,"Tests\\Feature\\Api\\PaymentControllerTest::it_completes_mock_payment_successfully":0.083,"Tests\\Feature\\Api\\PaymentControllerTest::it_checks_transaction_status":0.05,"Tests\\Feature\\Services\\WalletServiceTest::it_creates_deposit_transaction":0.003,"Tests\\Feature\\Services\\WalletServiceTest::it_completes_deposit_and_updates_balance":0.008,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_deposit":0.005,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_manual_withdrawal":0.007,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_withdrawal_when_balance_is_insufficient":0.004,"Tests\\Feature\\Services\\WalletServiceTest::it_processes_order_payment":0.004,"Tests\\Feature\\Services\\WalletServiceTest::it_prevents_order_payment_when_balance_is_insufficient":0.002,"Tests\\Feature\\Services\\WalletServiceTest::it_fails_transaction":0.005}}
|
||||
@ -15,6 +15,9 @@ class ShipmentReviewService
|
||||
{
|
||||
/**
|
||||
* درخواست اصلاح سفارش توسط کارمند.
|
||||
*
|
||||
* این عملیات یک Review Revision جدید ایجاد میکند،
|
||||
* چون یک submission جدید برای بررسی ثبت شده است.
|
||||
*/
|
||||
public function requestChanges(
|
||||
Shipment $shipment,
|
||||
@ -55,6 +58,9 @@ class ShipmentReviewService
|
||||
|
||||
/**
|
||||
* تأیید سفارش توسط کارمند.
|
||||
*
|
||||
* تأیید، Review جدید ایجاد نمیکند.
|
||||
* آخرین revision با وضعیت pending را finalize میکند.
|
||||
*/
|
||||
public function approve(
|
||||
Shipment $shipment,
|
||||
@ -72,8 +78,9 @@ class ShipmentReviewService
|
||||
'این سفارش در وضعیت قابل تأیید نیست.'
|
||||
);
|
||||
|
||||
$review = $this->getPendingReview($shipment);
|
||||
|
||||
$oldStatus = $shipment->status;
|
||||
$revisionNo = $this->nextReviewRevision($shipment);
|
||||
|
||||
$shipment->update([
|
||||
'status' => ShipmentStatus::Approved,
|
||||
@ -88,9 +95,7 @@ class ShipmentReviewService
|
||||
'changed_by' => $user->id,
|
||||
]);
|
||||
|
||||
ShipmentReview::create([
|
||||
'shipment_id' => $shipment->id,
|
||||
'revision_no' => $revisionNo,
|
||||
$review->update([
|
||||
'decision' => ReviewState::Approved->value,
|
||||
'reason' => null,
|
||||
'notes' => $notes,
|
||||
@ -104,6 +109,9 @@ class ShipmentReviewService
|
||||
|
||||
/**
|
||||
* رد سفارش توسط کارمند.
|
||||
*
|
||||
* رد، Review جدید ایجاد نمیکند.
|
||||
* آخرین revision با وضعیت pending را finalize میکند.
|
||||
*/
|
||||
public function reject(
|
||||
Shipment $shipment,
|
||||
@ -121,8 +129,9 @@ class ShipmentReviewService
|
||||
'این سفارش در وضعیت قابل رد نیست.'
|
||||
);
|
||||
|
||||
$review = $this->getPendingReview($shipment);
|
||||
|
||||
$oldStatus = $shipment->status;
|
||||
$revisionNo = $this->nextReviewRevision($shipment);
|
||||
|
||||
$shipment->update([
|
||||
'status' => ShipmentStatus::Cancelled,
|
||||
@ -137,9 +146,7 @@ class ShipmentReviewService
|
||||
'changed_by' => $user->id,
|
||||
]);
|
||||
|
||||
ShipmentReview::create([
|
||||
'shipment_id' => $shipment->id,
|
||||
'revision_no' => $revisionNo,
|
||||
$review->update([
|
||||
'decision' => ReviewState::Rejected->value,
|
||||
'reason' => $reason,
|
||||
'notes' => null,
|
||||
@ -152,10 +159,7 @@ class ShipmentReviewService
|
||||
}
|
||||
|
||||
/**
|
||||
* بررسی وضعیت فعلی برای عملیات Review.
|
||||
*
|
||||
* این check داخل transaction و بعد از lock انجام میشود
|
||||
* تا race condition بین دو کارمند کاهش یابد.
|
||||
* بررسی وضعیت فعلی Shipment برای عملیات Review.
|
||||
*/
|
||||
private function assertPendingReview(
|
||||
Shipment $shipment,
|
||||
@ -170,7 +174,31 @@ class ShipmentReviewService
|
||||
}
|
||||
|
||||
/**
|
||||
* تعیین revision بعدی.
|
||||
* پیدا کردن Review فعلی که منتظر تصمیم کارمند است.
|
||||
*
|
||||
* بعد از resubmit باید دقیقاً یک revision با decision=pending
|
||||
* وجود داشته باشد.
|
||||
*/
|
||||
private function getPendingReview(Shipment $shipment): ShipmentReview
|
||||
{
|
||||
$review = $shipment->reviews()
|
||||
->where('decision', ReviewState::Pending->value)
|
||||
->orderByDesc('revision_no')
|
||||
->first();
|
||||
|
||||
if (!$review) {
|
||||
throw new RuntimeException(
|
||||
'برای این سفارش هیچ Review در وضعیت pending پیدا نشد.'
|
||||
);
|
||||
}
|
||||
|
||||
return $review;
|
||||
}
|
||||
|
||||
/**
|
||||
* تعیین شماره revision بعدی.
|
||||
*
|
||||
* هر resubmission یک revision جدید ایجاد میکند.
|
||||
*/
|
||||
private function nextReviewRevision(Shipment $shipment): int
|
||||
{
|
||||
|
||||
36
04_Laravel/resubmit-test.json
Normal file
36
04_Laravel/resubmit-test.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"direction": "export",
|
||||
"type": "PARCEL",
|
||||
"from_country_id": 96,
|
||||
"to_country_id": 7,
|
||||
"weight": 3.70,
|
||||
"dimensions": "90*90*90",
|
||||
"packages": [
|
||||
{
|
||||
"weight": 3.70,
|
||||
"dimensions": "90*90*90",
|
||||
"description": "dfasdf"
|
||||
}
|
||||
],
|
||||
"sender_name": "Moshtari Updated",
|
||||
"sender_phone": "989898989899",
|
||||
"sender_address": "New addresssss",
|
||||
"sender_city": "asdfasdf",
|
||||
"receiver_name": "moshtari2",
|
||||
"receiver_phone": "565656565",
|
||||
"receiver_address": "sdfhsdfsdhsdgh",
|
||||
"receiver_city": "dfgsdfgsdfgs",
|
||||
"extra_service": 0,
|
||||
"packing_cost": 0,
|
||||
"domestic_pickup": 0,
|
||||
"domestic_delivery": 0,
|
||||
"warehousing_cost": 0,
|
||||
"items": [
|
||||
{
|
||||
"description": "HHHH25",
|
||||
"hs_code": "989898989",
|
||||
"quantity": 12,
|
||||
"unit_price": 1.20
|
||||
}
|
||||
]
|
||||
}
|
||||
36
resubmit-test.json
Normal file
36
resubmit-test.json
Normal file
@ -0,0 +1,36 @@
|
||||
{
|
||||
"direction": "export",
|
||||
"type": "PARCEL",
|
||||
"from_country_id": 96,
|
||||
"to_country_id": 7,
|
||||
"weight": 3.70,
|
||||
"dimensions": "90*90*90",
|
||||
"packages": [
|
||||
{
|
||||
"weight": 3.70,
|
||||
"dimensions": "90*90*90",
|
||||
"description": "dfasdf"
|
||||
}
|
||||
],
|
||||
"sender_name": "Moshtari Updated",
|
||||
"sender_phone": "989898989899",
|
||||
"sender_address": "New addresssss",
|
||||
"sender_city": "asdfasdf",
|
||||
"receiver_name": "moshtari2",
|
||||
"receiver_phone": "565656565",
|
||||
"receiver_address": "sdfhsdfsdhsdgh",
|
||||
"receiver_city": "dfgsdfgsdfgs",
|
||||
"extra_service": 0,
|
||||
"packing_cost": 0,
|
||||
"domestic_pickup": 0,
|
||||
"domestic_delivery": 0,
|
||||
"warehousing_cost": 0,
|
||||
"items": [
|
||||
{
|
||||
"description": "HHHH25",
|
||||
"hs_code": "989898989",
|
||||
"quantity": 12,
|
||||
"unit_price": 1.20
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user