refactor
This commit is contained in:
1 parent
6f36db63a1
commit
250cdbab7f
42 files changed
+96363
-11699
No files matched your search
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
تستهای اطمینان انبار: ریاضی موجودی، تاریخچه گردش کالا و چرخهٔ کامل انتقال بین انبارها.
|
||||
"""
|
||||
from django.core.exceptions import PermissionDenied, ValidationError
|
||||
from django.test import TestCase
|
||||
|
||||
from admin_panel.models import (ProductInStore, ProductStockHistory,
|
||||
StoreTransferRequest)
|
||||
from admin_panel.services import (approve_transfer_request,
|
||||
cancel_transfer_request,
|
||||
create_transfer_request,
|
||||
get_product_current_stock,
|
||||
reject_transfer_request)
|
||||
|
||||
from .factories import (current_stock, make_product, make_store, make_user,
|
||||
stock_in)
|
||||
|
||||
|
||||
class ProductInStoreMathTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.store = make_store('انبار مرکزی')
|
||||
self.product = make_product('کالای تست', price=500)
|
||||
|
||||
def test_initial_in_records_history_and_stock(self):
|
||||
row = stock_in(self.store, self.product, 10)
|
||||
|
||||
row.refresh_from_db()
|
||||
self.assertEqual(row.current_stock_at_time, 10)
|
||||
self.assertEqual(current_stock(self.store, self.product), 10)
|
||||
|
||||
history = ProductStockHistory.objects.filter(store_room=self.store, product=self.product)
|
||||
self.assertEqual(history.count(), 1)
|
||||
self.assertEqual(history.first().quantity_changed, 10)
|
||||
self.assertEqual(history.first().current_stock, 10)
|
||||
|
||||
def test_multiple_movements_accumulate_correctly(self):
|
||||
stock_in(self.store, self.product, 10)
|
||||
ProductInStore.objects.create(
|
||||
StoreRoom=self.store, Product=self.product, s_in=0, s_out=4,
|
||||
desc='خروج تست', movement_type=ProductInStore.MovementType.DIRECT_OUT,
|
||||
)
|
||||
ProductInStore.objects.create(
|
||||
StoreRoom=self.store, Product=self.product, s_in=7, s_out=0,
|
||||
desc='ورود تست', movement_type=ProductInStore.MovementType.DIRECT_IN,
|
||||
)
|
||||
|
||||
self.assertEqual(current_stock(self.store, self.product), 13)
|
||||
self.assertEqual(get_product_current_stock(self.store, self.product), 13)
|
||||
self.assertEqual(
|
||||
ProductStockHistory.objects.filter(store_room=self.store, product=self.product).count(),
|
||||
3,
|
||||
)
|
||||
|
||||
def test_editing_a_row_updates_stock_and_history(self):
|
||||
row = stock_in(self.store, self.product, 10)
|
||||
|
||||
row.s_in = 25
|
||||
row.save()
|
||||
|
||||
row.refresh_from_db()
|
||||
self.assertEqual(row.current_stock_at_time, 25)
|
||||
self.assertEqual(current_stock(self.store, self.product), 25)
|
||||
|
||||
# آخرین رکورد تاریخچه باید تغییر (+15) را نشان دهد
|
||||
last = ProductStockHistory.objects.filter(
|
||||
store_room=self.store, product=self.product
|
||||
).order_by('-id').first()
|
||||
self.assertEqual(last.quantity_changed, 15)
|
||||
self.assertEqual(last.current_stock, 25)
|
||||
|
||||
def test_deleting_a_row_creates_history_and_reduces_stock(self):
|
||||
stock_in(self.store, self.product, 10)
|
||||
out_row = ProductInStore.objects.create(
|
||||
StoreRoom=self.store, Product=self.product, s_in=0, s_out=3,
|
||||
desc='خروج', movement_type=ProductInStore.MovementType.DIRECT_OUT,
|
||||
)
|
||||
self.assertEqual(current_stock(self.store, self.product), 7)
|
||||
|
||||
out_row.delete()
|
||||
|
||||
self.assertEqual(current_stock(self.store, self.product), 10)
|
||||
last = ProductStockHistory.objects.filter(
|
||||
store_room=self.store, product=self.product
|
||||
).order_by('-id').first()
|
||||
self.assertEqual(last.quantity_changed, 3)
|
||||
self.assertEqual(last.current_stock, 10)
|
||||
|
||||
def test_garbage_numeric_values_are_coerced_to_zero(self):
|
||||
row = ProductInStore.objects.create(
|
||||
StoreRoom=self.store, Product=self.product,
|
||||
s_in='abc', s_out=None, desc='مقدار خراب',
|
||||
)
|
||||
row.refresh_from_db()
|
||||
self.assertEqual(row.s_in, 0)
|
||||
self.assertEqual(row.s_out, 0)
|
||||
|
||||
|
||||
class TransferWorkflowTests(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.boss = make_user('boss', superuser=True)
|
||||
self.store_a = make_store('مبدا', viewers=[self.boss])
|
||||
self.store_b = make_store('مقصد', viewers=[self.boss])
|
||||
self.product = make_product('کالای انتقالی', price=1000)
|
||||
stock_in(self.store_a, self.product, 10)
|
||||
|
||||
def test_create_transfer_request_pending(self):
|
||||
transfer = create_transfer_request(
|
||||
source_store=self.store_a,
|
||||
target_store=self.store_b,
|
||||
product=self.product,
|
||||
quantity=4,
|
||||
requester=self.boss,
|
||||
)
|
||||
self.assertEqual(transfer.status, StoreTransferRequest.Status.PENDING)
|
||||
# تا زمان تایید، موجودی هیچ انباری تغییر نمیکند
|
||||
self.assertEqual(current_stock(self.store_a, self.product), 10)
|
||||
self.assertEqual(current_stock(self.store_b, self.product), 0)
|
||||
|
||||
def test_create_rejects_insufficient_stock(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
create_transfer_request(
|
||||
source_store=self.store_a, target_store=self.store_b,
|
||||
product=self.product, quantity=999, requester=self.boss,
|
||||
)
|
||||
|
||||
def test_create_rejects_same_store(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
create_transfer_request(
|
||||
source_store=self.store_a, target_store=self.store_a,
|
||||
product=self.product, quantity=1, requester=self.boss,
|
||||
)
|
||||
|
||||
def test_create_rejects_non_positive_quantity(self):
|
||||
with self.assertRaises(ValidationError):
|
||||
create_transfer_request(
|
||||
source_store=self.store_a, target_store=self.store_b,
|
||||
product=self.product, quantity=0, requester=self.boss,
|
||||
)
|
||||
|
||||
def test_approve_moves_stock_and_links_transactions(self):
|
||||
transfer = create_transfer_request(
|
||||
source_store=self.store_a, target_store=self.store_b,
|
||||
product=self.product, quantity=4, requester=self.boss,
|
||||
)
|
||||
|
||||
approved = approve_transfer_request(request_id=transfer.id, approver=self.boss)
|
||||
|
||||
self.assertEqual(approved.status, StoreTransferRequest.Status.APPROVED)
|
||||
self.assertIsNotNone(approved.out_transaction_id)
|
||||
self.assertIsNotNone(approved.in_transaction_id)
|
||||
self.assertEqual(current_stock(self.store_a, self.product), 6)
|
||||
self.assertEqual(current_stock(self.store_b, self.product), 4)
|
||||
|
||||
def test_double_approval_is_rejected(self):
|
||||
transfer = create_transfer_request(
|
||||
source_store=self.store_a, target_store=self.store_b,
|
||||
product=self.product, quantity=4, requester=self.boss,
|
||||
)
|
||||
approve_transfer_request(request_id=transfer.id, approver=self.boss)
|
||||
|
||||
with self.assertRaises(ValidationError):
|
||||
approve_transfer_request(request_id=transfer.id, approver=self.boss)
|
||||
|
||||
def test_reject_keeps_stock_untouched(self):
|
||||
transfer = create_transfer_request(
|
||||
source_store=self.store_a, target_store=self.store_b,
|
||||
product=self.product, quantity=4, requester=self.boss,
|
||||
)
|
||||
rejected = reject_transfer_request(request_id=transfer.id, rejector=self.boss, reason='کالا خراب')
|
||||
|
||||
self.assertEqual(rejected.status, StoreTransferRequest.Status.REJECTED)
|
||||
self.assertEqual(rejected.rejection_reason, 'کالا خراب')
|
||||
self.assertEqual(current_stock(self.store_a, self.product), 10)
|
||||
self.assertEqual(current_stock(self.store_b, self.product), 0)
|
||||
|
||||
def test_cancel_by_requester(self):
|
||||
transfer = create_transfer_request(
|
||||
source_store=self.store_a, target_store=self.store_b,
|
||||
product=self.product, quantity=4, requester=self.boss,
|
||||
)
|
||||
canceled = cancel_transfer_request(request_id=transfer.id, user=self.boss)
|
||||
self.assertEqual(canceled.status, StoreTransferRequest.Status.CANCELED)
|
||||
|
||||
def test_permission_denied_for_outsider(self):
|
||||
outsider = make_user('outsider')
|
||||
with self.assertRaises(PermissionDenied):
|
||||
create_transfer_request(
|
||||
source_store=self.store_a, target_store=self.store_b,
|
||||
product=self.product, quantity=1, requester=outsider,
|
||||
)
|
||||
|
||||
def test_approval_permission_denied_for_non_target_member(self):
|
||||
transfer = create_transfer_request(
|
||||
source_store=self.store_a, target_store=self.store_b,
|
||||
product=self.product, quantity=4, requester=self.boss,
|
||||
)
|
||||
outsider = make_user('outsider2')
|
||||
with self.assertRaises(PermissionDenied):
|
||||
approve_transfer_request(request_id=transfer.id, approver=outsider)
|
||||
Reference in new issue
Block a user