kpsul/kfet/tests/test_views.py
2022-10-03 10:55:33 +02:00

4849 lines
154 KiB
Python

import json
from datetime import datetime, timedelta
from decimal import Decimal
from unittest import mock
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.contrib.auth.models import User
from django.test import Client, TestCase
from django.urls import reverse
from django.utils import timezone
from .. import KFET_DELETED_TRIGRAMME
from ..auth import KFET_GENERIC_TRIGRAMME
from ..auth.models import KFetGroup
from ..auth.utils import hash_password
from ..config import kfet_config
from ..models import (
Account,
Article,
ArticleCategory,
Checkout,
CheckoutStatement,
Inventory,
InventoryArticle,
Operation,
OperationGroup,
Order,
OrderArticle,
Supplier,
SupplierArticle,
Transfer,
TransferGroup,
)
from .testcases import ViewTestCaseMixin
from .utils import (
create_checkout,
create_checkout_statement,
create_inventory_article,
create_operation_group,
create_team,
create_user,
get_perms,
user_add_perms,
)
class AccountListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account"
url_expected = "/k-fet/accounts/"
auth_user = "team"
auth_forbidden = [None, "user"]
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
class AccountValidFreeTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.is_validandfree.ajax"
url_expected = "/k-fet/accounts/is_validandfree"
auth_user = "team"
auth_forbidden = [None, "user"]
def test_ok_isvalid_isfree(self):
"""Upper case trigramme not taken is valid and free."""
r = self.client.get(self.url, {"trigramme": "AAA"})
self.assertDictEqual(
json.loads(r.content.decode("utf-8")), {"is_valid": True, "is_free": True}
)
def test_ok_isvalid_notfree(self):
"""Already taken trigramme is not free, but valid."""
r = self.client.get(self.url, {"trigramme": "000"})
self.assertDictEqual(
json.loads(r.content.decode("utf-8")), {"is_valid": True, "is_free": False}
)
def test_ok_notvalid_isfree(self):
"""Lower case if forbidden but free."""
r = self.client.get(self.url, {"trigramme": "aaa"})
self.assertDictEqual(
json.loads(r.content.decode("utf-8")), {"is_valid": False, "is_free": True}
)
class AccountCreateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.create"
url_expected = "/k-fet/accounts/new"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
post_data = {
"trigramme": "AAA",
"username": "plopplopplop",
"first_name": "first",
"last_name": "last",
"email": "email@domain.net",
}
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.add_account"])}
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
self.assertRedirects(r, reverse("kfet.account.create"))
account = Account.objects.get(trigramme="AAA")
self.assertInstanceExpected(
account,
{"username": "plopplopplop", "first_name": "first", "last_name": "last"},
)
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)
class AccountCreateAjaxViewTests(ViewTestCaseMixin, TestCase):
urls_conf = [
{
"name": "kfet.account.create.fromuser",
"kwargs": {"username": "user"},
"expected": "/k-fet/accounts/new/user/user",
},
{
"name": "kfet.account.create.fromclipper",
"kwargs": {"login_clipper": "myclipper", "fullname": "first last1 last2"},
"expected": ("/k-fet/accounts/new/clipper/myclipper/first%20last1%20last2"),
},
{"name": "kfet.account.create.empty", "expected": "/k-fet/accounts/new/empty"},
]
auth_user = "team"
auth_forbidden = [None, "user"]
def test_fromuser(self):
r = self.client.get(self.t_urls[0])
self.assertEqual(r.status_code, 200)
user = self.users["user"]
self.assertEqual(r.context["user_form"].instance, user)
self.assertEqual(r.context["cof_form"].instance, user.profile)
self.assertIn("account_form", r.context)
def test_fromclipper(self):
r = self.client.get(self.t_urls[1])
self.assertEqual(r.status_code, 200)
self.assertIn("user_form", r.context)
self.assertIn("cof_form", r.context)
self.assertIn("account_form", r.context)
def test_empty(self):
r = self.client.get(self.t_urls[2])
self.assertEqual(r.status_code, 200)
self.assertIn("user_form", r.context)
self.assertIn("cof_form", r.context)
self.assertIn("account_form", r.context)
class AccountCreateAutocompleteViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.create.autocomplete"
url_expected = "/k-fet/autocomplete/account_new"
auth_user = "team"
auth_forbidden = [None, "user"]
def test_ok(self):
r = self.client.get(self.url, {"q": "first"})
self.assertEqual(r.status_code, 200)
self.assertEqual(len(r.context["results"]), 1)
(res,) = r.context["results"]
self.assertEqual(res.name, "kfet")
u = self.users["user"]
self.assertSetEqual(
{e.verbose_name for e in res.entries},
{"{} ({})".format(u, u.profile.account_kfet.trigramme)},
)
class AccountSearchViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.search.autocomplete"
url_expected = "/k-fet/autocomplete/account_search"
auth_user = "team"
auth_forbidden = [None, "user"]
def test_ok(self):
r = self.client.get(self.url, {"q": "first"})
self.assertEqual(r.status_code, 200)
u = self.users["user"]
self.assertSetEqual(
{e.verbose_name for e in r.context["results"][0].entries},
{"{} ({})".format(u, u.profile.account_kfet.trigramme)},
)
class AccountReadViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.read"
url_kwargs = {"trigramme": "001"}
url_expected = "/k-fet/accounts/001"
auth_user = "team"
auth_forbidden = [None, "user"]
# Users with forbidden access users should get a 404 here, to avoid leaking trigrams
# See issue #224
def test_forbidden(self):
for user in self.auth_forbidden:
self.assertRedirectsToLoginOr404(user, self.url_expected)
self.assertRedirectsToLoginOr404(user, "/k-fet/accounts/NEX")
def assertRedirectsToLoginOr404(self, user, url):
client = Client()
if user is None:
response = client.get(url)
self.assertRedirects(
response,
"/gestion/login?next={}".format(url),
fetch_redirect_response=False,
)
else:
client.login(username=user, password=user)
response = client.get(url)
self.assertEqual(response.status_code, 404)
def get_users_extra(self):
return {"user1": create_user("user1", "001")}
def setUp(self):
super().setUp()
user1_acc = self.accounts["user1"]
team_acc = self.accounts["team"]
# Dummy operations and operation groups
checkout = Checkout.objects.create(
created_by=team_acc,
name="checkout",
valid_from=timezone.now(),
valid_to=timezone.now() + timezone.timedelta(days=365),
)
opeg_data = [
(timezone.now(), Decimal("10")),
(timezone.now() - timezone.timedelta(days=3), Decimal("3")),
]
OperationGroup.objects.bulk_create(
[
OperationGroup(
on_acc=user1_acc,
checkout=checkout,
at=at,
is_cof=False,
amount=amount,
)
for (at, amount) in opeg_data
]
)
self.operation_groups = OperationGroup.objects.order_by("-amount")
Operation.objects.create(
group=self.operation_groups[0],
type=Operation.PURCHASE,
amount=Decimal("10"),
)
Operation.objects.create(
group=self.operation_groups[1], type=Operation.PURCHASE, amount=Decimal("3")
)
def test_ok(self):
"""We can query the "Account - Read" page."""
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_ok_self(self):
client = Client()
client.login(username="user1", password="user1")
r = client.get(self.url)
self.assertEqual(r.status_code, 200)
class AccountUpdateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.update"
url_kwargs = {"trigramme": "100"}
url_expected = "/k-fet/accounts/100/edit"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
post_data = {
# User
"first_name": "The first",
"last_name": "The last",
"email": "",
# Group
"groups[]": [],
# Account
"trigramme": "051",
"nickname": "",
"promo": "",
# 'is_frozen': not checked
# Account password
"pwd1": "changed_pwd",
"pwd2": "changed_pwd",
}
def get_users_extra(self):
return {
"team1": create_team("team1", "101", perms=["kfet.change_account"]),
"team2": create_team("team2", "102"),
}
def assertRedirectsToLoginOr404(self, user, method, url):
client = Client()
meth = getattr(client, method)
if user is None:
response = meth(url)
self.assertRedirects(
response,
"/gestion/login?next={}".format(url),
fetch_redirect_response=False,
)
else:
client.login(username=user, password=user)
response = meth(url)
self.assertEqual(response.status_code, 404)
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data, follow=True)
self.assertRedirects(r, reverse("kfet.account.read", args=["051"]))
# Comportement attendu : compte modifié,
# utilisateur/mdp inchangé, warning pour le mdp
self.accounts["team"].refresh_from_db()
self.users["team"].refresh_from_db()
self.assertInstanceExpected(
self.accounts["team"],
{"first_name": "team", "last_name": "member", "trigramme": "051"},
)
self.assertEqual(self.accounts["team"].password, hash_password("kfetpwd_team"))
self.assertTrue(
any("mot de passe" in str(msg).casefold() for msg in r.context["messages"])
)
def test_post_ok_self(self):
r = self.client.post(self.url, self.post_data, follow=True)
self.assertRedirects(r, reverse("kfet.account.read", args=["051"]))
self.accounts["team"].refresh_from_db()
self.users["team"].refresh_from_db()
# Comportement attendu : compte/mdp modifié, utilisateur inchangé
self.assertInstanceExpected(
self.accounts["team"],
{"first_name": "team", "last_name": "member", "trigramme": "051"},
)
self.assertEqual(self.accounts["team"].password, hash_password("changed_pwd"))
def test_post_forbidden(self):
client = Client()
client.login(username="team2", password="team2")
r = client.post(self.url, self.post_data)
self.assertTrue(
any(
"permission refusée" in str(msg).casefold()
for msg in r.context["messages"]
)
)
class AccountDeleteViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.delete"
url_kwargs = {"trigramme": "001"}
url_expected = "/k-fet/accounts/001/delete"
auth_user = "team1"
auth_forbidden = [None, "user", "team"]
http_methods = ["GET", "POST"]
with_liq = True
def get_users_extra(self):
return {
"user1": create_user("user1", "001"),
"team1": create_team("team1", "101", perms=["kfet.delete_account"]),
"trez": create_user("trez", "#13"),
}
def test_get_405(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 405)
def test_post_ok(self):
r = self.client.post(self.url, {})
self.assertRedirects(r, reverse("kfet.account"))
with self.assertRaises(Account.DoesNotExist):
self.accounts["user1"].refresh_from_db()
def test_protected_accounts(self):
for trigramme in ["LIQ", "#13", KFET_GENERIC_TRIGRAMME, KFET_DELETED_TRIGRAMME]:
if Account.objects.get(trigramme=trigramme).readable:
expected_code = 200
else:
expected_code = 404
r = self.client.post(
reverse(self.url_name, kwargs={"trigramme": trigramme}), {}
)
self.assertRedirects(
r,
reverse("kfet.account.read", kwargs={"trigramme": trigramme}),
target_status_code=expected_code,
)
# Devrait être redondant avec le précédent, mais on sait jamais
self.assertTrue(Account.objects.filter(trigramme=trigramme).exists())
def test_nonempty_accounts(self):
self.accounts["user1"].balance = 1
self.accounts["user1"].save()
r = self.client.post(self.url, {})
self.assertRedirects(r, reverse("kfet.account.read", kwargs=self.url_kwargs))
# Shouldn't throw an error
self.accounts["user1"].refresh_from_db()
class AccountGroupListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.group"
url_expected = "/k-fet/accounts/groups"
auth_user = "team1"
auth_forbidden = [None, "user", "team"]
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.manage_perms"])}
def setUp(self):
super().setUp()
self.group1 = KFetGroup.objects.create(name="Group1")
self.group2 = KFetGroup.objects.create(name="Group2")
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertQuerysetEqual(
r.context["groups"],
[self.group1.pk, self.group2.pk],
transform=lambda group: group.pk,
ordered=False,
)
class AccountGroupCreateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.group.create"
url_expected = "/k-fet/accounts/groups/new"
http_methods = ["GET", "POST"]
auth_user = "team1"
auth_forbidden = [None, "user", "team"]
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.manage_perms"])}
@property
def post_data(self):
return {
"name": "The Group",
"permissions": [
str(self.perms["kfet.is_team"].pk),
str(self.perms["kfet.manage_perms"].pk),
],
}
def setUp(self):
super().setUp()
self.perms = get_perms("kfet.is_team", "kfet.manage_perms")
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
r = self.client.post(self.url, self.post_data)
self.assertRedirects(r, reverse("kfet.account.group"))
group = KFetGroup.objects.get(name="The Group")
self.assertQuerysetEqual(
group.permissions.all(),
map(repr, [self.perms["kfet.is_team"], self.perms["kfet.manage_perms"]]),
ordered=False,
)
class AccountGroupUpdateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.group.update"
http_methods = ["GET", "POST"]
auth_user = "team1"
auth_forbidden = [None, "user", "team"]
@property
def url_kwargs(self):
return {"pk": self.group.pk}
@property
def url_expected(self):
return "/k-fet/accounts/groups/{}/edit".format(self.group.pk)
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.manage_perms"])}
@property
def post_data(self):
return {
"name": "The Group",
"permissions": [
str(self.perms["kfet.is_team"].pk),
str(self.perms["kfet.manage_perms"].pk),
],
}
def setUp(self):
super().setUp()
self.perms = get_perms("kfet.is_team", "kfet.manage_perms")
self.group = KFetGroup.objects.create(name="Group")
self.group.permissions.set(self.perms.values())
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
r = self.client.post(self.url, self.post_data)
self.assertRedirects(r, reverse("kfet.account.group"))
self.group.refresh_from_db()
self.assertEqual(self.group.name, "The Group")
self.assertQuerysetEqual(
self.group.permissions.all(),
map(repr, [self.perms["kfet.is_team"], self.perms["kfet.manage_perms"]]),
ordered=False,
)
class AccountNegativeListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.negative"
url_expected = "/k-fet/accounts/negatives"
auth_user = "team1"
auth_forbidden = [None, "user", "team"]
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.view_negs"])}
def setUp(self):
super().setUp()
account = self.accounts["user"]
account.balance = -5
account.save()
account.update_negative()
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertQuerysetEqual(
r.context["negatives"],
map(repr, [self.accounts["user"].negative]),
ordered=False,
)
class AccountStatOperationListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.stat.operation.list"
url_kwargs = {"trigramme": "001"}
url_expected = "/k-fet/accounts/001/stat/operations/list"
auth_user = "user1"
auth_forbidden = [None, "user", "team"]
def get_users_extra(self):
return {"user1": create_user("user1", "001")}
# Users with forbidden access users should get a 404 here, to avoid leaking trigrams
# See issue #224
def test_forbidden(self):
for user in self.auth_forbidden:
self.assertRedirectsToLoginOr404(user, self.url_expected)
self.assertRedirectsToLoginOr404(
user, "/k-fet/accounts/NEX/stat/operations/list"
)
def assertRedirectsToLoginOr404(self, user, url):
client = Client()
if user is None:
response = client.get(url)
self.assertRedirects(
response,
"/gestion/login?next={}".format(url),
fetch_redirect_response=False,
)
else:
client.login(username=user, password=user)
response = client.get(url)
self.assertEqual(response.status_code, 404)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
content = json.loads(r.content.decode("utf-8"))
base_url = reverse("kfet.account.stat.operation", args=["001"])
expected_stats = [
{
"label": "Tout le temps",
"url": {
"path": base_url,
"query": {
"scale-name": ["month"],
"scale-last": ["True"],
"scale-begin": [
self.accounts["user1"]
.created_at.replace(tzinfo=None)
.isoformat(" ")
],
},
},
},
{
"label": "1 an",
"url": {
"path": base_url,
"query": {
"scale-n_steps": ["12"],
"scale-name": ["month"],
"scale-last": ["True"],
},
},
},
{
"label": "3 mois",
"url": {
"path": base_url,
"query": {
"scale-n_steps": ["13"],
"scale-name": ["week"],
"scale-last": ["True"],
},
},
},
{
"label": "2 semaines",
"url": {
"path": base_url,
"query": {
"scale-n_steps": ["14"],
"scale-name": ["day"],
"scale-last": ["True"],
},
},
},
]
for stat, expected in zip(content["stats"], expected_stats):
expected_url = expected.pop("url")
self.assertUrlsEqual(stat["url"], expected_url)
self.assertDictContainsSubset(expected, stat)
class AccountStatOperationViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.stat.operation"
url_kwargs = {"trigramme": "001"}
url_expected = "/k-fet/accounts/001/stat/operations"
auth_user = "user1"
auth_forbidden = [None, "user", "team"]
# Users with forbidden access users should get a 404 here, to avoid leaking trigrams
# See issue #224
def test_forbidden(self):
for user in self.auth_forbidden:
self.assertRedirectsToLoginOr404(user, self.url_expected)
self.assertRedirectsToLoginOr404(
user, "/k-fet/accounts/NEX/stat/operations"
)
def assertRedirectsToLoginOr404(self, user, url):
client = Client()
if user is None:
response = client.get(url)
self.assertRedirects(
response,
"/gestion/login?next={}".format(url),
fetch_redirect_response=False,
)
else:
client.login(username=user, password=user)
response = client.get(url)
self.assertEqual(response.status_code, 404)
def get_users_extra(self):
return {"user1": create_user("user1", "001")}
def test_ok(self):
r = self.client.get(
self.url, {"scale-name": "day", "scale-n_steps": 7, "scale-last": True}
)
self.assertEqual(r.status_code, 200)
class AccountStatBalanceListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.stat.balance.list"
url_kwargs = {"trigramme": "001"}
url_expected = "/k-fet/accounts/001/stat/balance/list"
auth_user = "user1"
auth_forbidden = [None, "user", "team"]
# Users with forbidden access users should get a 404 here, to avoid leaking trigrams
# See issue #224
def test_forbidden(self):
for user in self.auth_forbidden:
self.assertRedirectsToLoginOr404(user, self.url_expected)
self.assertRedirectsToLoginOr404(
user, "/k-fet/accounts/NEX/stat/balance/list"
)
def assertRedirectsToLoginOr404(self, user, url):
client = Client()
if user is None:
response = client.get(url)
self.assertRedirects(
response,
"/gestion/login?next={}".format(url),
fetch_redirect_response=False,
)
else:
client.login(username=user, password=user)
response = client.get(url)
self.assertEqual(response.status_code, 404)
def get_users_extra(self):
return {"user1": create_user("user1", "001")}
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
content = json.loads(r.content.decode("utf-8"))
base_url = reverse("kfet.account.stat.balance", args=["001"])
expected_stats = [
{"label": "Tout le temps", "url": base_url},
{
"label": "1 an",
"url": {"path": base_url, "query": {"last_days": ["365"]}},
},
{
"label": "6 mois",
"url": {"path": base_url, "query": {"last_days": ["183"]}},
},
{
"label": "3 mois",
"url": {"path": base_url, "query": {"last_days": ["90"]}},
},
{
"label": "30 jours",
"url": {"path": base_url, "query": {"last_days": ["30"]}},
},
]
for stat, expected in zip(content["stats"], expected_stats):
expected_url = expected.pop("url")
self.assertUrlsEqual(stat["url"], expected_url)
self.assertDictContainsSubset(expected, stat)
class AccountStatBalanceViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.stat.balance"
url_kwargs = {"trigramme": "001"}
url_expected = "/k-fet/accounts/001/stat/balance"
auth_user = "user1"
auth_forbidden = [None, "user", "team"]
# Users with forbidden access users should get a 404 here, to avoid leaking trigrams
# See issue #224
def test_forbidden(self):
for user in self.auth_forbidden:
self.assertRedirectsToLoginOr404(user, self.url_expected)
self.assertRedirectsToLoginOr404(user, "/k-fet/accounts/NEX/stat/balance")
def assertRedirectsToLoginOr404(self, user, url):
client = Client()
if user is None:
response = client.get(url)
self.assertRedirects(
response,
"/gestion/login?next={}".format(url),
fetch_redirect_response=False,
)
else:
client.login(username=user, password=user)
response = client.get(url)
self.assertEqual(response.status_code, 404)
def get_users_extra(self):
return {"user1": create_user("user1", "001")}
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
class CheckoutListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.checkout"
url_expected = "/k-fet/checkouts/"
auth_user = "team"
auth_forbidden = [None, "user"]
def setUp(self):
super().setUp()
self.checkout1 = Checkout.objects.create(
name="Checkout 1",
created_by=self.accounts["team"],
valid_from=self.now,
valid_to=self.now + timedelta(days=5),
)
self.checkout2 = Checkout.objects.create(
name="Checkout 2",
created_by=self.accounts["team"],
valid_from=self.now + timedelta(days=10),
valid_to=self.now + timedelta(days=15),
)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertQuerysetEqual(
r.context["checkouts"],
map(repr, [self.checkout1, self.checkout2]),
ordered=False,
)
class CheckoutCreateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.checkout.create"
url_expected = "/k-fet/checkouts/new"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
post_data = {
"name": "Checkout",
"valid_from": "2017-10-08 17:45:00",
"valid_to": "2017-11-08 16:00:00",
"balance": "3.14",
# 'is_protected': not checked
}
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.add_checkout"])}
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
checkout = Checkout.objects.get(name="Checkout")
self.assertRedirects(r, checkout.get_absolute_url())
self.assertInstanceExpected(
checkout,
{
"name": "Checkout",
"valid_from": timezone.make_aware(datetime(2017, 10, 8, 17, 45)),
"valid_to": timezone.make_aware(datetime(2017, 11, 8, 16, 00)),
"balance": Decimal("3.14"),
"is_protected": False,
},
)
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)
class CheckoutReadViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.checkout.read"
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"pk": self.checkout.pk}
@property
def url_expected(self):
return "/k-fet/checkouts/{}".format(self.checkout.pk)
def setUp(self):
super().setUp()
with mock.patch("django.utils.timezone.now") as mock_now:
mock_now.return_value = self.now
self.checkout = Checkout.objects.create(
name="Checkout",
balance=Decimal("10"),
created_by=self.accounts["team"],
valid_from=self.now,
valid_to=self.now + timedelta(days=1),
)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.context["checkout"], self.checkout)
class CheckoutUpdateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.checkout.update"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
post_data = {
"name": "Checkout updated",
"valid_from": "2018-01-01 08:00:00",
"valid_to": "2018-07-01 16:00:00",
}
@property
def url_kwargs(self):
return {"pk": self.checkout.pk}
@property
def url_expected(self):
return "/k-fet/checkouts/{}/edit".format(self.checkout.pk)
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.change_checkout"])}
def setUp(self):
super().setUp()
self.checkout = Checkout.objects.create(
name="Checkout",
valid_from=self.now,
valid_to=self.now + timedelta(days=5),
balance=Decimal("3.14"),
is_protected=False,
created_by=self.accounts["team"],
)
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
self.assertRedirects(r, self.checkout.get_absolute_url())
self.checkout.refresh_from_db()
self.assertInstanceExpected(
self.checkout,
{
"name": "Checkout updated",
"valid_from": timezone.make_aware(datetime(2018, 1, 1, 8, 0, 0)),
"valid_to": timezone.make_aware(datetime(2018, 7, 1, 16, 0, 0)),
},
)
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)
class CheckoutStatementListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.checkoutstatement"
url_expected = "/k-fet/checkouts/statements/"
auth_user = "team"
auth_forbidden = [None, "user"]
def setUp(self):
super().setUp()
self.checkout1 = Checkout.objects.create(
created_by=self.accounts["team"],
name="Checkout 1",
valid_from=self.now,
valid_to=self.now + timedelta(days=5),
)
self.checkout2 = Checkout.objects.create(
created_by=self.accounts["team"],
name="Checkout 2",
valid_from=self.now + timedelta(days=10),
valid_to=self.now + timedelta(days=15),
)
self.statement1 = CheckoutStatement.objects.create(
checkout=self.checkout1,
by=self.accounts["team"],
balance_old=5,
balance_new=0,
amount_taken=5,
)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
expected_statements = list(self.checkout1.statements.all()) + list(
self.checkout2.statements.all()
)
self.assertQuerysetEqual(
r.context["checkoutstatements"],
map(repr, expected_statements),
ordered=False,
)
class CheckoutStatementCreateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.checkoutstatement.create"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
post_data = {
# Let
"balance_001": 0,
"balance_002": 0,
"balance_005": 0,
"balance_01": 0,
"balance_02": 0,
"balance_05": 0,
"balance_1": 1,
"balance_2": 0,
"balance_5": 0,
"balance_10": 1,
"balance_20": 0,
"balance_50": 0,
"balance_100": 1,
"balance_200": 0,
"balance_500": 0,
# Taken
"taken_001": 0,
"taken_002": 0,
"taken_005": 0,
"taken_01": 0,
"taken_02": 0,
"taken_05": 0,
"taken_1": 2,
"taken_2": 0,
"taken_5": 0,
"taken_10": 2,
"taken_20": 0,
"taken_50": 0,
"taken_100": 2,
"taken_200": 0,
"taken_500": 0,
"taken_cheque": 0,
# 'not_count': not checked
}
@property
def url_kwargs(self):
return {"pk_checkout": self.checkout.pk}
@property
def url_expected(self):
return "/k-fet/checkouts/{}/statements/add".format(self.checkout.pk)
def get_users_extra(self):
return {
"team1": create_team("team1", "001", perms=["kfet.add_checkoutstatement"])
}
def setUp(self):
super().setUp()
self.checkout = Checkout.objects.create(
name="Checkout",
created_by=self.accounts["team"],
balance=5,
valid_from=self.now,
valid_to=self.now + timedelta(days=5),
)
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
@mock.patch("django.utils.timezone.now")
def test_post_ok(self, mock_now):
self.now += timedelta(days=2)
mock_now.return_value = self.now
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
self.assertRedirects(r, self.checkout.get_absolute_url())
statement = CheckoutStatement.objects.get(at=self.now)
self.assertInstanceExpected(
statement,
{
"by": self.accounts["team1"],
"checkout": self.checkout,
"balance_old": Decimal("5"),
"balance_new": Decimal("111"),
"amount_taken": Decimal("222"),
"amount_error": Decimal("328"),
"at": self.now,
"not_count": False,
},
)
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)
class CheckoutStatementUpdateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.checkoutstatement.update"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
post_data = {
"amount_taken": 3,
"amount_error": 2,
"balance_old": 8,
"balance_new": 5,
# Taken
"taken_001": 0,
"taken_002": 0,
"taken_005": 0,
"taken_01": 0,
"taken_02": 0,
"taken_05": 0,
"taken_1": 1,
"taken_2": 1,
"taken_5": 0,
"taken_10": 0,
"taken_20": 0,
"taken_50": 0,
"taken_100": 0,
"taken_200": 0,
"taken_500": 0,
"taken_cheque": 0,
}
@property
def url_kwargs(self):
return {"pk_checkout": self.checkout.pk, "pk": self.statement.pk}
@property
def url_expected(self):
return "/k-fet/checkouts/{pk_checkout}/statements/{pk}/edit".format(
pk_checkout=self.checkout.pk, pk=self.statement.pk
)
def get_users_extra(self):
return {
"team1": create_team(
"team1", "101", perms=["kfet.change_checkoutstatement"]
)
}
def setUp(self):
super().setUp()
self.checkout = Checkout.objects.create(
name="Checkout",
created_by=self.accounts["team"],
balance=5,
valid_from=self.now,
valid_to=self.now + timedelta(days=5),
)
self.statement = CheckoutStatement.objects.create(
by=self.accounts["team"],
checkout=self.checkout,
balance_new=5,
balance_old=8,
amount_error=2,
amount_taken=5,
)
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
@mock.patch("django.utils.timezone.now")
def test_post_ok(self, mock_now):
self.now += timedelta(days=2)
mock_now.return_value = self.now
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
self.assertRedirects(r, self.checkout.get_absolute_url())
self.statement.refresh_from_db()
self.assertInstanceExpected(
self.statement,
{
"taken_1": 1,
"taken_2": 1,
"balance_new": 5,
"balance_old": 8,
"amount_error": 0,
"amount_taken": 3,
},
)
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)
class ArticleCategoryListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.category"
url_expected = "/k-fet/categories/"
auth_user = "team"
auth_forbidden = [None, "user"]
def setUp(self):
super().setUp()
self.category1 = ArticleCategory.objects.create(name="Category 1")
self.category2 = ArticleCategory.objects.create(name="Category 2")
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertQuerysetEqual(
r.context["categories"], map(repr, [self.category1, self.category2])
)
class ArticleCategoryUpdateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.category.update"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"pk": self.category.pk}
@property
def url_expected(self):
return "/k-fet/categories/{}/edit".format(self.category.pk)
def get_users_extra(self):
return {
"team1": create_team("team1", "101", perms=["kfet.change_articlecategory"])
}
@property
def post_data(self):
return {
"name": "The Category",
# 'has_addcost': not checked
}
def setUp(self):
super().setUp()
self.category = ArticleCategory.objects.create(name="Category")
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
self.assertRedirects(r, reverse("kfet.category"))
self.category.refresh_from_db()
self.assertInstanceExpected(
self.category, {"name": "The Category", "has_addcost": False}
)
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)
class ArticleListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.article"
url_expected = "/k-fet/articles/"
auth_user = "team"
auth_forbidden = [None, "user"]
def setUp(self):
super().setUp()
category = ArticleCategory.objects.create(name="Category")
self.article1 = Article.objects.create(name="Article 1", category=category)
self.article2 = Article.objects.create(name="Article 2", category=category)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertQuerysetEqual(
r.context["articles"], map(repr, [self.article1, self.article2])
)
class ArticleCreateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.article.create"
url_expected = "/k-fet/articles/new"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.add_article"])}
@property
def post_data(self):
return {
"name": "Article",
"category": self.category.pk,
"stock": 5,
"price": "2.5",
}
def setUp(self):
super().setUp()
self.category = ArticleCategory.objects.create(name="Category")
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
article = Article.objects.get(name="Article")
self.assertRedirects(r, article.get_absolute_url())
self.assertInstanceExpected(
article, {"name": "Article", "category": self.category}
)
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)
class ArticleReadViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.article.read"
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"pk": self.article.pk}
@property
def url_expected(self):
return "/k-fet/articles/{}".format(self.article.pk)
def setUp(self):
super().setUp()
self.article = Article.objects.create(
name="Article",
category=ArticleCategory.objects.create(name="Category"),
stock=5,
price=Decimal("2.5"),
)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
self.assertEqual(r.context["article"], self.article)
class ArticleUpdateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.article.update"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"pk": self.article.pk}
@property
def url_expected(self):
return "/k-fet/articles/{}/edit".format(self.article.pk)
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.change_article"])}
@property
def post_data(self):
return {
"name": "The Article",
"category": self.article.category.pk,
"is_sold": "1",
"price": "3.5",
"box_type": "carton",
# 'hidden': not checked
}
def setUp(self):
super().setUp()
self.category = ArticleCategory.objects.create(name="Category")
self.article = Article.objects.create(
name="Article", category=self.category, stock=5, price=Decimal("2.5")
)
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
self.assertRedirects(r, self.article.get_absolute_url())
self.article.refresh_from_db()
self.assertInstanceExpected(
self.article, {"name": "The Article", "price": Decimal("3.5")}
)
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)
class ArticleDeleteViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.article.delete"
auth_user = "team1"
auth_forbidden = [None, "user", "team"]
@property
def url_kwargs(self):
return {"pk": self.article.pk}
@property
def url_expected(self):
return "/k-fet/articles/{}/delete".format(self.article.pk)
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.delete_article"])}
def setUp(self):
super().setUp()
self.category = ArticleCategory.objects.create(name="Category")
self.article = Article.objects.create(
name="Article", category=self.category, stock=5, price=Decimal("2.5")
)
def test_get_redirects(self):
r = self.client.get(self.url)
self.assertRedirects(r, reverse("kfet.article.read", kwargs=self.url_kwargs))
def test_post_ok(self):
r = self.client.post(self.url, {})
self.assertRedirects(r, reverse("kfet.article"))
with self.assertRaises(Article.DoesNotExist):
self.article.refresh_from_db()
class ArticleStatSalesListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.article.stat.sales.list"
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"pk": self.article.pk}
@property
def url_expected(self):
return "/k-fet/articles/{}/stat/sales/list".format(self.article.pk)
def setUp(self):
super().setUp()
self.article = Article.objects.create(
name="Article", category=ArticleCategory.objects.create(name="Category")
)
checkout = Checkout.objects.create(
name="Checkout",
created_by=self.accounts["team"],
balance=5,
valid_from=self.now,
valid_to=self.now + timedelta(days=5),
)
self.opegroup = create_operation_group(
on_acc=self.accounts["user"],
checkout=checkout,
content=[
{"type": Operation.PURCHASE, "article": self.article, "article_nb": 2},
],
)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
content = json.loads(r.content.decode("utf-8"))
base_url = reverse("kfet.article.stat.sales", args=[self.article.pk])
expected_stats = [
{
"label": "Tout le temps",
"url": {
"path": base_url,
"query": {
"scale-name": ["month"],
"scale-last": ["True"],
"scale-begin": [self.opegroup.at.strftime("%Y-%m-%d %H:%M:%S")],
},
},
},
{
"label": "1 an",
"url": {
"path": base_url,
"query": {
"scale-n_steps": ["12"],
"scale-name": ["month"],
"scale-last": ["True"],
},
},
},
{
"label": "3 mois",
"url": {
"path": base_url,
"query": {
"scale-n_steps": ["13"],
"scale-name": ["week"],
"scale-last": ["True"],
},
},
},
{
"label": "2 semaines",
"url": {
"path": base_url,
"query": {
"scale-n_steps": ["14"],
"scale-name": ["day"],
"scale-last": ["True"],
},
},
},
]
for stat, expected in zip(content["stats"], expected_stats):
expected_url = expected.pop("url")
self.assertUrlsEqual(stat["url"], expected_url)
self.assertDictContainsSubset(expected, stat)
class ArticleStatSalesViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.article.stat.sales"
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"pk": self.article.pk}
@property
def url_expected(self):
return "/k-fet/articles/{}/stat/sales".format(self.article.pk)
def setUp(self):
super().setUp()
self.article = Article.objects.create(
name="Article", category=ArticleCategory.objects.create(name="Category")
)
def test_ok(self):
r = self.client.get(
self.url, {"scale-name": "day", "scale-n_steps": 7, "scale-last": True}
)
self.assertEqual(r.status_code, 200)
class KPsulViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.kpsul"
url_expected = "/k-fet/k-psul/"
auth_user = "team"
auth_forbidden = [None, "user"]
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
class KPsulCheckoutDataViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.kpsul.checkout_data"
url_expected = "/k-fet/k-psul/checkout_data"
http_methods = ["POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
def setUp(self):
super().setUp()
self.checkout = Checkout.objects.create(
name="Checkout",
balance=Decimal("10"),
created_by=self.accounts["team"],
valid_from=self.now,
valid_to=self.now + timedelta(days=5),
)
def test_ok(self):
r = self.client.post(self.url, {"pk": self.checkout.pk})
self.assertEqual(r.status_code, 200)
content = json.loads(r.content.decode("utf-8"))
expected = {"name": "Checkout", "balance": "10.00"}
self.assertDictContainsSubset(expected, content)
self.assertSetEqual(
set(content.keys()),
set(
[
"balance",
"id",
"name",
"valid_from",
"valid_to",
"last_statement_at",
"last_statement_balance",
"last_statement_by_first_name",
"last_statement_by_last_name",
"last_statement_by_trigramme",
]
),
)
class KPsulPerformOperationsViewTests(ViewTestCaseMixin, TestCase):
"""
Test cases for kpsul_perform_operations view.
Below is the test ordering, try to keep this organized ;-)
* OperationGroup:
- test_group...
- test_invalid_group...
* Operation:
- test_purchase...
- test_invalid_purchase...
- test_deposit...
- test_invalid_deposit...
- test_withdraw...
- test_invalid_withdraw...
- test_edit...
- test_invalid_edit...
* Addcost:
- test_addcost...
* Negative:
- test_negative...
- test_invalid_negative...
* More concrete examples:
- test_multi...
To test valid requests, one should use '_assertResponseOk(response)' to get
hints about failure reasons, if any.
At least one test per operation type should test the complete response and
behavior (HTTP, WebSocket, object updates, and object creations)
Other tests of the same operation type can only assert the specific
behavior differences.
For invalid requests, response errors should be tested.
"""
url_name = "kfet.kpsul.perform_operations"
url_expected = "/k-fet/k-psul/perform_operations"
http_methods = ["POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
with_liq = True
def setUp(self):
super(KPsulPerformOperationsViewTests, self).setUp()
# A Checkout, curently usable, balance=100
self.checkout = Checkout.objects.create(
created_by=self.accounts["team"],
name="Checkout",
valid_from=timezone.now() - timedelta(days=7),
valid_to=timezone.now() + timedelta(days=7),
balance=Decimal("100.00"),
)
# An Article, price=2.5, stock=20
self.article = Article.objects.create(
category=ArticleCategory.objects.create(name="Category"),
name="Article",
price=Decimal("2.5"),
stock=20,
)
# Another Article, price=2.5, stock=20, no COF reduction
self.article_no_reduction = Article.objects.create(
category=ArticleCategory.objects.create(
name="Category_no_reduction",
has_reduction=False,
),
name="Article_no_reduction",
price=Decimal("2.5"),
stock=20,
)
# An Account, trigramme=000, balance=50
# Do not assume user is cof, nor not cof.
self.account = self.accounts["user"]
self.account.balance = Decimal("50.00")
self.account.save()
# Create a channel to listen to KPsul's messages
channel_layer = get_channel_layer()
self.channel = async_to_sync(channel_layer.new_channel)()
async_to_sync(channel_layer.group_add)("kfet.kpsul", self.channel)
self.receive_msg = lambda: async_to_sync(channel_layer.receive)(self.channel)
# Reset cache of kfet config
kfet_config._conf_init = False
def _assertResponseOk(self, response):
"""
Asserts that status code of 'response' is 200, and returns the
deserialized content of the JSONResponse.
In case status code is not 200, it prints the content of "errors" of
the response.
"""
json_data = json.loads(getattr(response, "content", b"{}").decode("utf-8"))
try:
self.assertEqual(response.status_code, 200)
except AssertionError as exc:
msg = "Expected response is 200, got {}. Errors: {}".format(
response.status_code, json_data.get("errors")
)
raise AssertionError(msg) from exc
return json_data
def get_base_post_data(self):
return {
# OperationGroup form
"on_acc": str(self.account.pk),
"checkout": str(self.checkout.pk),
# Operation formset
"form-TOTAL_FORMS": "0",
"form-INITIAL_FORMS": "0",
"form-MIN_NUM_FORMS": "1",
"form-MAX_NUM_FORMS": "1000",
}
base_post_data = property(get_base_post_data)
def test_invalid_group_on_acc(self):
data = dict(self.base_post_data, **{"on_acc": "GNR"})
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_on_acc", "invalid_formset"],
)
def test_group_on_acc_expects_comment(self):
user_add_perms(self.users["team"], ["kfet.perform_commented_operations"])
self.account.trigramme = "#13"
self.account.save()
self.assertTrue(self.account.need_comment)
data = dict(
self.base_post_data,
**{
"comment": "A comment to explain it",
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
def test_invalid_group_on_acc_expects_comment(self):
user_add_perms(self.users["team"], ["kfet.perform_commented_operations"])
self.account.trigramme = "#13"
self.account.save()
self.assertTrue(self.account.need_comment)
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertEqual(json_data["need_comment"], True)
def test_invalid_group_on_acc_needs_comment_requires_perm(self):
self.account.trigramme = "#13"
self.account.save()
self.assertTrue(self.account.need_comment)
data = dict(
self.base_post_data,
**{
"comment": "A comment to explain it",
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 403)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertEqual(
json_data["missing_perms"],
["Enregistrer des commandes avec commentaires"],
)
def test_error_on_acc_frozen(self):
self.account.is_frozen = True
self.account.save()
data = dict(
self.base_post_data,
**{
"comment": "A comment to explain it",
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertEqual([e["code"] for e in json_data["errors"]], ["frozen_acc"])
def test_invalid_group_checkout(self):
self.checkout.valid_from -= timedelta(days=300)
self.checkout.valid_to -= timedelta(days=300)
self.checkout.save()
data = dict(self.base_post_data)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_checkout", "invalid_formset"],
)
def test_invalid_group_expects_one_operation(self):
data = dict(self.base_post_data)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_formset"],
)
def test_purchase_with_user_is_nof_cof(self):
self.account.cofprofile.is_cof = False
self.account.cofprofile.save()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
# Check response status
json_data = self._assertResponseOk(resp)
# Check object creations
operation_group = OperationGroup.objects.get()
self.assertDictEqual(
operation_group.__dict__,
{
"_state": mock.ANY,
"at": mock.ANY,
"amount": Decimal("-5.00"),
"checkout_id": self.checkout.pk,
"comment": "",
"id": mock.ANY,
"is_cof": False,
"on_acc_id": self.account.pk,
"valid_by_id": None,
},
)
operation = Operation.objects.get()
self.assertDictEqual(
operation.__dict__,
{
"_state": mock.ANY,
"addcost_amount": None,
"addcost_for_id": None,
"amount": Decimal("-5.00"),
"article_id": self.article.pk,
"article_nb": 2,
"canceled_at": None,
"canceled_by_id": None,
"group_id": operation_group.pk,
"id": mock.ANY,
"type": "purchase",
},
)
# Check response content
self.assertDictEqual(
json_data,
{"errors": []},
)
# Check object updates
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("45.00"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("100.00"))
self.article.refresh_from_db()
self.assertEqual(self.article.stock, 18)
# Check websocket data
ws_data = self.receive_msg()
self.assertDictEqual(
ws_data,
{
"type": "kpsul",
"groups": [
{
"add": True,
"type": "operation",
"at": mock.ANY,
"amount": Decimal("-5.00"),
"checkout__name": "Checkout",
"comment": "",
"id": operation_group.pk,
"is_cof": False,
"on_acc__trigramme": "000",
"valid_by__trigramme": None,
"entries": [
{
"id": operation.pk,
"addcost_amount": None,
"addcost_for__trigramme": None,
"amount": Decimal("-5.00"),
"article__name": "Article",
"article_nb": 2,
"canceled_at": None,
"canceled_by__trigramme": None,
"group_id": operation_group.pk,
"type": "purchase",
}
],
}
],
"checkouts": [{"id": self.checkout.pk, "balance": Decimal("100.00")}],
"articles": [{"id": self.article.pk, "stock": 18}],
},
)
def test_purchase_with_user_is_cof(self):
kfet_config.set(kfet_reduction_cof=Decimal("20"))
self.account.cofprofile.is_cof = True
self.account.cofprofile.save()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
operation_group = OperationGroup.objects.get()
self.assertEqual(operation_group.amount, Decimal("-4.00"))
self.assertEqual(operation_group.is_cof, True)
operation = Operation.objects.get()
self.assertEqual(operation.amount, Decimal("-4.00"))
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("46.00"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("100.00"))
self.article.refresh_from_db()
self.assertEqual(self.article.stock, 18)
def test_purchase_with_cash(self):
data = dict(
self.base_post_data,
**{
"on_acc": str(self.accounts["liq"].pk),
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
operation_group = OperationGroup.objects.get()
self.assertEqual(operation_group.on_acc, self.accounts["liq"])
self.assertEqual(operation_group.is_cof, False)
self.accounts["liq"].refresh_from_db()
self.assertEqual(self.accounts["liq"].balance, 0)
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("105.00"))
self.article.refresh_from_db()
self.assertEqual(self.article.stock, 18)
def test_purchase_no_reduction(self):
kfet_config.set(kfet_reduction_cof=Decimal("20"))
self.account.cofprofile.is_cof = True
self.account.cofprofile.save()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "2",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article_no_reduction.pk),
"form-0-article_nb": "1",
"form-1-type": "purchase",
"form-1-amount": "",
"form-1-article": str(self.article.pk),
"form-1-article_nb": "1",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
operation_group = OperationGroup.objects.get()
self.assertEqual(operation_group.amount, Decimal("-4.50"))
operation = Operation.objects.get(article=self.article)
self.assertEqual(operation.amount, Decimal("-2.00"))
operation = Operation.objects.get(article=self.article_no_reduction)
self.assertEqual(operation.amount, Decimal("-2.50"))
def test_invalid_purchase_expects_article(self):
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": "",
"form-0-article_nb": "1",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_formset"],
)
def test_invalid_purchase_expects_article_nb(self):
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_formset"],
)
def test_invalid_purchase_expects_article_nb_greater_than_1(self):
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "-1",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_formset"],
)
def test_invalid_operation_not_purchase_with_cash(self):
data = dict(
self.base_post_data,
**{
"on_acc": str(self.accounts["liq"].pk),
"form-TOTAL_FORMS": "1",
"form-0-type": "deposit",
"form-0-amount": "10.00",
"form-0-article": "",
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_liq"],
)
def test_deposit(self):
user_add_perms(self.users["team"], ["kfet.perform_deposit"])
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "deposit",
"form-0-amount": "10.75",
"form-0-article": "",
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
operation_group = OperationGroup.objects.get()
self.assertDictEqual(
operation_group.__dict__,
{
"_state": mock.ANY,
"at": mock.ANY,
"amount": Decimal("10.75"),
"checkout_id": self.checkout.pk,
"comment": "",
"id": mock.ANY,
"is_cof": False,
"on_acc_id": self.account.pk,
"valid_by_id": self.accounts["team"].pk,
},
)
operation = Operation.objects.get()
self.assertDictEqual(
operation.__dict__,
{
"_state": mock.ANY,
"addcost_amount": None,
"addcost_for_id": None,
"amount": Decimal("10.75"),
"article_id": None,
"article_nb": None,
"canceled_at": None,
"canceled_by_id": None,
"group_id": operation_group.pk,
"id": mock.ANY,
"type": "deposit",
},
)
self.assertDictEqual(
json_data,
{"errors": []},
)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("60.75"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("110.75"))
ws_data = self.receive_msg()
self.assertDictEqual(
ws_data,
{
"type": "kpsul",
"groups": [
{
"add": True,
"type": "operation",
"at": mock.ANY,
"amount": Decimal("10.75"),
"checkout__name": "Checkout",
"comment": "",
"id": operation_group.pk,
"is_cof": False,
"on_acc__trigramme": "000",
"valid_by__trigramme": "100",
"entries": [
{
"id": operation.pk,
"addcost_amount": None,
"addcost_for__trigramme": None,
"amount": Decimal("10.75"),
"article__name": None,
"article_nb": None,
"canceled_at": None,
"canceled_by__trigramme": None,
"group_id": operation_group.pk,
"type": "deposit",
}
],
}
],
"checkouts": [{"id": self.checkout.pk, "balance": Decimal("110.75")}],
"articles": [],
},
)
def test_invalid_deposit_expects_amount(self):
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "deposit",
"form-0-amount": "",
"form-0-article": "",
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_formset"],
)
def test_invalid_deposit_too_many_params(self):
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "deposit",
"form-0-amount": "10",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "3",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_formset"],
)
def test_invalid_deposit_expects_positive_amount(self):
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "deposit",
"form-0-amount": "-10",
"form-0-article": "",
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_formset"],
)
def test_invalid_deposit_requires_perm(self):
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "deposit",
"form-0-amount": "10.75",
"form-0-article": "",
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 403)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertEqual(json_data["missing_perms"], ["Effectuer une charge"])
def test_withdraw(self):
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "withdraw",
"form-0-amount": "-10.75",
"form-0-article": "",
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
operation_group = OperationGroup.objects.get()
self.assertDictEqual(
operation_group.__dict__,
{
"_state": mock.ANY,
"at": mock.ANY,
"amount": Decimal("-10.75"),
"checkout_id": self.checkout.pk,
"comment": "",
"id": mock.ANY,
"is_cof": False,
"on_acc_id": self.account.pk,
"valid_by_id": None,
},
)
operation = Operation.objects.get()
self.assertDictEqual(
operation.__dict__,
{
"_state": mock.ANY,
"addcost_amount": None,
"addcost_for_id": None,
"amount": Decimal("-10.75"),
"article_id": None,
"article_nb": None,
"canceled_at": None,
"canceled_by_id": None,
"group_id": operation_group.pk,
"id": mock.ANY,
"type": "withdraw",
},
)
self.assertDictEqual(
json_data,
{"errors": []},
)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("39.25"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("89.25"))
ws_data = self.receive_msg()
self.assertDictEqual(
ws_data,
{
"type": "kpsul",
"groups": [
{
"add": True,
"type": "operation",
"at": mock.ANY,
"amount": Decimal("-10.75"),
"checkout__name": "Checkout",
"comment": "",
"id": operation_group.pk,
"is_cof": False,
"on_acc__trigramme": "000",
"valid_by__trigramme": None,
"entries": [
{
"id": operation.pk,
"addcost_amount": None,
"addcost_for__trigramme": None,
"amount": Decimal("-10.75"),
"article__name": None,
"article_nb": None,
"canceled_at": None,
"canceled_by__trigramme": None,
"group_id": operation_group.pk,
"type": "withdraw",
}
],
}
],
"checkouts": [{"id": self.checkout.pk, "balance": Decimal("89.25")}],
"articles": [],
},
)
def test_invalid_withdraw_expects_amount(self):
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "withdraw",
"form-0-amount": "",
"form-0-article": "",
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_formset"],
)
def test_invalid_withdraw_too_many_params(self):
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "withdraw",
"form-0-amount": "-10",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "3",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_formset"],
)
def test_invalid_withdraw_expects_negative_amount(self):
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "withdraw",
"form-0-amount": "10",
"form-0-article": "",
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_formset"],
)
def test_edit(self):
user_add_perms(self.users["team"], ["kfet.edit_balance_account"])
data = dict(
self.base_post_data,
**{
"comment": "A comment to explain it",
"form-TOTAL_FORMS": "1",
"form-0-type": "edit",
"form-0-amount": "10.75",
"form-0-article": "",
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
operation_group = OperationGroup.objects.get()
self.assertDictEqual(
operation_group.__dict__,
{
"_state": mock.ANY,
"at": mock.ANY,
"amount": Decimal("10.75"),
"checkout_id": self.checkout.pk,
"comment": "A comment to explain it",
"id": mock.ANY,
"is_cof": False,
"on_acc_id": self.account.pk,
"valid_by_id": self.accounts["team"].pk,
},
)
operation = Operation.objects.get()
self.assertDictEqual(
operation.__dict__,
{
"_state": mock.ANY,
"addcost_amount": None,
"addcost_for_id": None,
"amount": Decimal("10.75"),
"article_id": None,
"article_nb": None,
"canceled_at": None,
"canceled_by_id": None,
"group_id": operation_group.pk,
"id": mock.ANY,
"type": "edit",
},
)
self.assertDictEqual(
json_data,
{"errors": []},
)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("60.75"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("100.00"))
ws_data = self.receive_msg()
self.assertDictEqual(
ws_data,
{
"type": "kpsul",
"groups": [
{
"add": True,
"type": "operation",
"at": mock.ANY,
"amount": Decimal("10.75"),
"checkout__name": "Checkout",
"comment": "A comment to explain it",
"id": operation_group.pk,
"is_cof": False,
"on_acc__trigramme": "000",
"valid_by__trigramme": "100",
"entries": [
{
"id": operation.pk,
"addcost_amount": None,
"addcost_for__trigramme": None,
"amount": Decimal("10.75"),
"article__name": None,
"article_nb": None,
"canceled_at": None,
"canceled_by__trigramme": None,
"group_id": operation_group.pk,
"type": "edit",
}
],
}
],
"checkouts": [{"id": self.checkout.pk, "balance": Decimal("100.00")}],
"articles": [],
},
)
def test_invalid_edit_requires_perm(self):
data = dict(
self.base_post_data,
**{
"comment": "A comment to explain it",
"form-TOTAL_FORMS": "1",
"form-0-type": "edit",
"form-0-amount": "10.75",
"form-0-article": "",
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 403)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertEqual(
json_data["missing_perms"],
["Modifier la balance d'un compte"],
)
def test_invalid_edit_expects_comment(self):
user_add_perms(self.users["team"], ["kfet.edit_balance_account"])
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "edit",
"form-0-amount": "10.75",
"form-0-article": "",
"form-0-article_nb": "",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertEqual(json_data["need_comment"], True)
def _setup_addcost(self):
self.register_user("addcost", create_user("addcost", "ADD"))
kfet_config.set(
addcost_amount=Decimal("0.50"), addcost_for=self.accounts["addcost"]
)
def test_addcost_user_is_not_cof(self):
self.account.cofprofile.is_cof = False
self.account.cofprofile.save()
self._setup_addcost()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
operation_group = OperationGroup.objects.get()
self.assertEqual(operation_group.amount, Decimal("-6.00"))
operation = Operation.objects.get()
self.assertEqual(operation.addcost_for, self.accounts["addcost"])
self.assertEqual(operation.addcost_amount, Decimal("1.00"))
self.assertEqual(operation.amount, Decimal("-6.00"))
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("44.00"))
self.accounts["addcost"].refresh_from_db()
self.assertEqual(self.accounts["addcost"].balance, Decimal("1.00"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("100.00"))
ws_data = self.receive_msg()
ws_data_ope = ws_data["groups"][0]["entries"][0]
self.assertEqual(ws_data_ope["addcost_amount"], Decimal("1.00"))
self.assertEqual(ws_data_ope["addcost_for__trigramme"], "ADD")
def test_addcost_user_is_cof(self):
kfet_config.set(reduction_cof=Decimal("20"))
self.account.cofprofile.is_cof = True
self.account.cofprofile.save()
self._setup_addcost()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
operation_group = OperationGroup.objects.get()
self.assertEqual(operation_group.amount, Decimal("-4.80"))
operation = Operation.objects.get()
self.assertEqual(operation.addcost_for, self.accounts["addcost"])
self.assertEqual(operation.addcost_amount, Decimal("0.80"))
self.assertEqual(operation.amount, Decimal("-4.80"))
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("45.20"))
self.accounts["addcost"].refresh_from_db()
self.assertEqual(self.accounts["addcost"].balance, Decimal("0.80"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("100.00"))
ws_data = self.receive_msg()
ws_data_ope = ws_data["groups"][0]["entries"][0]
self.assertEqual(ws_data_ope["addcost_amount"], Decimal("0.80"))
self.assertEqual(ws_data_ope["addcost_for__trigramme"], "ADD")
def test_addcost_user_is_cash(self):
self.account.cofprofile.is_cof = True
self.account.cofprofile.save()
self._setup_addcost()
data = dict(
self.base_post_data,
**{
"on_acc": str(self.accounts["liq"].pk),
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
operation_group = OperationGroup.objects.get()
self.assertEqual(operation_group.amount, Decimal("-6.00"))
operation = Operation.objects.get()
self.assertEqual(operation.addcost_for, self.accounts["addcost"])
self.assertEqual(operation.addcost_amount, Decimal("1.00"))
self.assertEqual(operation.amount, Decimal("-6.00"))
self.accounts["addcost"].refresh_from_db()
self.assertEqual(self.accounts["addcost"].balance, Decimal("1.00"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("106.00"))
ws_data = self.receive_msg()
ws_data_ope = ws_data["groups"][0]["entries"][0]
self.assertEqual(ws_data_ope["addcost_amount"], Decimal("1.00"))
self.assertEqual(ws_data_ope["addcost_for__trigramme"], "ADD")
def test_addcost_to_self(self):
self._setup_addcost()
self.accounts["addcost"].balance = Decimal("20.00")
self.accounts["addcost"].save()
data = dict(
self.base_post_data,
**{
"on_acc": str(self.accounts["addcost"].pk),
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
operation_group = OperationGroup.objects.get()
self.assertEqual(operation_group.amount, Decimal("-5.00"))
operation = Operation.objects.get()
self.assertEqual(operation.addcost_for, None)
self.assertEqual(operation.addcost_amount, None)
self.assertEqual(operation.amount, Decimal("-5.00"))
self.accounts["addcost"].refresh_from_db()
self.assertEqual(self.accounts["addcost"].balance, Decimal("15.00"))
ws_data = self.receive_msg()
ws_data_ope = ws_data["groups"][0]["entries"][0]
self.assertEqual(ws_data_ope["addcost_amount"], None)
self.assertEqual(ws_data_ope["addcost_for__trigramme"], None)
def test_addcost_category_disabled(self):
self._setup_addcost()
self.article.category.has_addcost = False
self.article.category.save()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
operation_group = OperationGroup.objects.get()
self.assertEqual(operation_group.amount, Decimal("-5.00"))
operation = Operation.objects.get()
self.assertEqual(operation.addcost_for, None)
self.assertEqual(operation.addcost_amount, None)
self.assertEqual(operation.amount, Decimal("-5.00"))
self.accounts["addcost"].refresh_from_db()
self.assertEqual(self.accounts["addcost"].balance, Decimal("0.00"))
ws_data = self.receive_msg()
ws_data_ope = ws_data["groups"][0]["entries"][0]
self.assertEqual(ws_data_ope["addcost_amount"], None)
self.assertEqual(ws_data_ope["addcost_for__trigramme"], None)
def test_negative_new(self):
user_add_perms(self.users["team"], ["kfet.perform_negative_operations"])
self.account.balance = Decimal("1.00")
self.account.save()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("-4.00"))
def test_negative_exists(self):
user_add_perms(self.users["team"], ["kfet.perform_negative_operations"])
self.account.balance = Decimal("-10.00")
self.account.save()
self.account.update_negative()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("-15.00"))
def test_negative_exists_balance_higher_than_initial(self):
user_add_perms(self.users["team"], ["kfet.perform_deposit"])
self.account.balance = Decimal("-10.00")
self.account.save()
self.account.update_negative()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "2",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "1",
"form-1-type": "deposit",
"form-1-amount": "5.00",
"form-1-article": "",
"form-1-article_nb": "",
}
)
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("-7.50"))
def test_invalid_negative_new_requires_perm(self):
self.account.balance = Decimal("1.00")
self.account.save()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 403)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertEqual(
json_data["missing_perms"],
["Enregistrer des commandes en négatif"],
)
def test_invalid_negative_exceeds_amount_allowed_from_config(self):
user_add_perms(self.users["team"], ["kfet.perform_negative_operations"])
kfet_config.set(overdraft_amount=Decimal("-1.00"))
self.account.balance = Decimal("1.00")
self.account.save()
self.account.update_negative()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "1",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
}
)
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["negative"],
)
def test_multi_0(self):
article2 = Article.objects.create(
name="Article 2",
price=Decimal("4"),
stock=-5,
category=ArticleCategory.objects.first(),
)
self.account.cofprofile.is_cof = False
self.account.cofprofile.save()
data = dict(
self.base_post_data,
**{
"form-TOTAL_FORMS": "2",
"form-0-type": "purchase",
"form-0-amount": "",
"form-0-article": str(self.article.pk),
"form-0-article_nb": "2",
"form-1-type": "purchase",
"form-1-amount": "",
"form-1-article": str(article2.pk),
"form-1-article_nb": "1",
}
)
resp = self.client.post(self.url, data)
# Check response status
json_data = self._assertResponseOk(resp)
# Check object creations
operation_group = OperationGroup.objects.get()
self.assertDictEqual(
operation_group.__dict__,
{
"_state": mock.ANY,
"at": mock.ANY,
"amount": Decimal("-9.00"),
"checkout_id": self.checkout.pk,
"comment": "",
"id": mock.ANY,
"is_cof": False,
"on_acc_id": self.account.pk,
"valid_by_id": None,
},
)
operation_list = Operation.objects.all()
self.assertEqual(len(operation_list), 2)
self.assertDictEqual(
operation_list[0].__dict__,
{
"_state": mock.ANY,
"addcost_amount": None,
"addcost_for_id": None,
"amount": Decimal("-5.00"),
"article_id": self.article.pk,
"article_nb": 2,
"canceled_at": None,
"canceled_by_id": None,
"group_id": operation_group.pk,
"id": mock.ANY,
"type": "purchase",
},
)
self.assertDictEqual(
operation_list[1].__dict__,
{
"_state": mock.ANY,
"addcost_amount": None,
"addcost_for_id": None,
"amount": Decimal("-4.00"),
"article_id": article2.pk,
"article_nb": 1,
"canceled_at": None,
"canceled_by_id": None,
"group_id": operation_group.pk,
"id": mock.ANY,
"type": "purchase",
},
)
# Check response content
self.assertDictEqual(
json_data,
{"errors": []},
)
# Check object updates
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("41.00"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("100.00"))
self.article.refresh_from_db()
self.assertEqual(self.article.stock, 18)
article2.refresh_from_db()
self.assertEqual(article2.stock, -6)
# Check websocket data
ws_data = self.receive_msg()
self.assertDictEqual(
ws_data,
{
"type": "kpsul",
"groups": [
{
"add": True,
"type": "operation",
"at": mock.ANY,
"amount": Decimal("-9.00"),
"checkout__name": "Checkout",
"comment": "",
"id": operation_group.pk,
"is_cof": False,
"on_acc__trigramme": "000",
"valid_by__trigramme": None,
"entries": [
{
"id": operation_list[0].pk,
"addcost_amount": None,
"addcost_for__trigramme": None,
"amount": Decimal("-5.00"),
"article__name": "Article",
"article_nb": 2,
"canceled_at": None,
"canceled_by__trigramme": None,
"group_id": operation_group.pk,
"type": "purchase",
},
{
"id": operation_list[1].pk,
"addcost_amount": None,
"addcost_for__trigramme": None,
"amount": Decimal("-4.00"),
"article__name": "Article 2",
"article_nb": 1,
"canceled_at": None,
"canceled_by__trigramme": None,
"group_id": operation_group.pk,
"type": "purchase",
},
],
}
],
"checkouts": [{"id": self.checkout.pk, "balance": Decimal("100.00")}],
"articles": [
{"id": self.article.pk, "stock": 18},
{"id": article2.pk, "stock": -6},
],
},
)
class KPsulCancelOperationsViewTests(ViewTestCaseMixin, TestCase):
"""
Test cases for kpsul_cancel_operations view.
To test valid requests, one should use '_assertResponseOk(response)' to get
hints about failure reasons, if any.
At least one test per operation type should test the complete response and
behavior (HTTP, WebSocket, object updates, and object creations)
Other tests of the same operation type can only assert the specific
behavior differences.
For invalid requests, response errors should be tested.
"""
url_name = "kfet.operations.cancel"
url_expected = "/k-fet/k-psul/cancel_operations"
http_methods = ["POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
with_liq = True
def setUp(self):
super(KPsulCancelOperationsViewTests, self).setUp()
self.checkout = create_checkout(balance=Decimal("100.00"))
# An Article, price=2.5, stock=20
self.article = Article.objects.create(
category=ArticleCategory.objects.create(name="Category"),
name="Article",
price=Decimal("2.5"),
stock=20,
)
# An Account, trigramme=000, balance=50
# Do not assume user is cof, nor not cof.
self.account = self.accounts["user"]
self.account.balance = Decimal("50.00")
self.account.save()
# Create a channel to listen to KPsul's messages
channel_layer = get_channel_layer()
self.channel = async_to_sync(channel_layer.new_channel)()
async_to_sync(channel_layer.group_add)("kfet.kpsul", self.channel)
self.receive_msg = lambda: async_to_sync(channel_layer.receive)(self.channel)
def _assertResponseOk(self, response):
"""
Asserts that status code of 'response' is 200, and returns the
deserialized content of the JSONResponse.
In case status code is not 200, it prints the content of "errors" of
the response.
"""
json_data = json.loads(getattr(response, "content", b"{}").decode("utf-8"))
try:
self.assertEqual(response.status_code, 200)
except AssertionError as exc:
msg = "Expected response is 200, got {}. Errors: {}".format(
response.status_code, json_data.get("errors")
)
raise AssertionError(msg) from exc
return json_data
def test_invalid_operation_not_int(self):
data = {"operations[]": ["a"]}
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["invalid_request"],
)
def test_invalid_operation_not_exist(self):
data = {"operations[]": ["1000"]}
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["cancel_missing"],
)
@mock.patch("django.utils.timezone.now")
def test_purchase(self, now_mock):
now_mock.return_value = self.now
group = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[
{
"type": Operation.PURCHASE,
"article": self.article,
"article_nb": 2,
}
],
)
operation = group.opes.get()
now_mock.return_value += timedelta(seconds=15)
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
group = OperationGroup.objects.get()
self.assertDictEqual(
group.__dict__,
{
"_state": mock.ANY,
"amount": Decimal("0.00"),
"at": mock.ANY,
"checkout_id": self.checkout.pk,
"comment": "",
"id": mock.ANY,
"is_cof": False,
"on_acc_id": self.account.pk,
"valid_by_id": None,
},
)
operation = Operation.objects.get()
self.assertDictEqual(
operation.__dict__,
{
"_state": mock.ANY,
"addcost_amount": None,
"addcost_for_id": None,
"amount": Decimal("-5.00"),
"article_id": self.article.pk,
"article_nb": 2,
"canceled_at": self.now + timedelta(seconds=15),
"canceled_by_id": None,
"group_id": group.pk,
"id": mock.ANY,
"type": Operation.PURCHASE,
},
)
self.assertDictEqual(
json_data,
{
"canceled": [
{
"id": operation.id,
# l'encodage des dates en JSON est relou...
"canceled_at": mock.ANY,
"canceled_by__trigramme": None,
}
],
"errors": [],
"warnings": {},
"opegroups_to_update": [
{
"id": group.pk,
"amount": str(group.amount),
"is_cof": group.is_cof,
}
],
},
)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("55.00"))
self.article.refresh_from_db()
self.assertEqual(self.article.stock, 22)
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("100.00"))
ws_data = self.receive_msg()
self.assertDictEqual(
ws_data,
{
"type": "kpsul",
"checkouts": [],
"articles": [{"id": self.article.pk, "stock": 22}],
},
)
def test_purchase_with_addcost(self):
# TODO(AD): L'état de la balance du compte destinataire de la majoration ne
# devrait pas empêcher l'annulation d'une opération.
addcost_user = create_user(
"addcost", "ADD", account_attrs={"balance": Decimal("10.00")}
)
addcost_account = addcost_user.profile.account_kfet
group = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[
{
"type": Operation.PURCHASE,
"article": self.article,
"article_nb": 2,
"amount": Decimal("-6.00"),
"addcost_amount": Decimal("1.00"),
"addcost_for": addcost_account,
}
],
)
operation = group.opes.get()
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("56.00"))
addcost_account.refresh_from_db()
self.assertEqual(addcost_account.balance, Decimal("9.00"))
def test_purchase_cash(self):
group = create_operation_group(
on_acc=self.accounts["liq"],
checkout=self.checkout,
content=[
{
"type": Operation.PURCHASE,
"article": self.article,
"article_nb": 2,
"amount": Decimal("-5.00"),
}
],
)
operation = group.opes.get()
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
self.assertEqual(self.accounts["liq"].balance, Decimal("0.00"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("95.00"))
ws_data = self.receive_msg()
self.assertListEqual(
ws_data["checkouts"],
[{"id": self.checkout.pk, "balance": Decimal("95.00")}],
)
def test_purchase_cash_with_addcost(self):
# TODO(AD): L'état de la balance du compte destinataire de la majoration ne
# devrait pas empêcher l'annulation d'une opération.
addcost_user = create_user(
"addcost", "ADD", account_attrs={"balance": Decimal("10.00")}
)
addcost_account = addcost_user.profile.account_kfet
group = create_operation_group(
on_acc=self.accounts["liq"],
checkout=self.checkout,
content=[
{
"type": Operation.PURCHASE,
"article": self.article,
"article_nb": 2,
"amount": Decimal("-6.00"),
"addcost_amount": Decimal("1.00"),
"addcost_for": addcost_account,
}
],
)
operation = group.opes.get()
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
self._assertResponseOk(resp)
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("94.00"))
addcost_account.refresh_from_db()
self.assertEqual(addcost_account.balance, Decimal("9.00"))
ws_data = self.receive_msg()
self.assertListEqual(
ws_data["checkouts"],
[{"id": self.checkout.pk, "balance": Decimal("94.00")}],
)
@mock.patch("django.utils.timezone.now")
def test_deposit(self, now_mock):
now_mock.return_value = self.now
group = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[{"type": Operation.DEPOSIT, "amount": Decimal("10.75")}],
)
operation = group.opes.get()
now_mock.return_value += timedelta(seconds=15)
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
group = OperationGroup.objects.get()
self.assertDictEqual(
group.__dict__,
{
"_state": mock.ANY,
"amount": Decimal("0.00"),
"at": mock.ANY,
"checkout_id": self.checkout.pk,
"comment": "",
"id": mock.ANY,
"is_cof": False,
"on_acc_id": self.account.pk,
"valid_by_id": None,
},
)
operation = Operation.objects.get()
self.assertDictEqual(
operation.__dict__,
{
"_state": mock.ANY,
"addcost_amount": None,
"addcost_for_id": None,
"amount": Decimal("10.75"),
"article_id": None,
"article_nb": None,
"canceled_at": self.now + timedelta(seconds=15),
"canceled_by_id": None,
"group_id": group.pk,
"id": mock.ANY,
"type": Operation.DEPOSIT,
},
)
self.assertDictEqual(
json_data,
{
"canceled": [
{
"id": operation.id,
# l'encodage des dates en JSON est relou...
"canceled_at": mock.ANY,
"canceled_by__trigramme": None,
}
],
"errors": [],
"warnings": {},
"opegroups_to_update": [
{
"id": group.pk,
"amount": str(group.amount),
"is_cof": group.is_cof,
}
],
},
)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("39.25"))
self.article.refresh_from_db()
self.assertEqual(self.article.stock, 20)
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("89.25"))
ws_data = self.receive_msg()
self.assertDictEqual(
ws_data,
{
"type": "kpsul",
"checkouts": [{"id": self.checkout.pk, "balance": Decimal("89.25")}],
"articles": [],
},
)
@mock.patch("django.utils.timezone.now")
def test_withdraw(self, now_mock):
now_mock.return_value = self.now
group = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[{"type": Operation.WITHDRAW, "amount": Decimal("-10.75")}],
)
operation = group.opes.get()
now_mock.return_value += timedelta(seconds=15)
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
group = OperationGroup.objects.get()
self.assertDictEqual(
group.__dict__,
{
"_state": mock.ANY,
"amount": Decimal("0.00"),
"at": mock.ANY,
"checkout_id": self.checkout.pk,
"comment": "",
"id": mock.ANY,
"is_cof": False,
"on_acc_id": self.account.pk,
"valid_by_id": None,
},
)
operation = Operation.objects.get()
self.assertDictEqual(
operation.__dict__,
{
"_state": mock.ANY,
"addcost_amount": None,
"addcost_for_id": None,
"amount": Decimal("-10.75"),
"article_id": None,
"article_nb": None,
"canceled_at": self.now + timedelta(seconds=15),
"canceled_by_id": None,
"group_id": group.pk,
"id": mock.ANY,
"type": Operation.WITHDRAW,
},
)
self.assertDictEqual(
json_data,
{
"canceled": [
{
"id": operation.id,
# l'encodage des dates en JSON est relou...
"canceled_at": mock.ANY,
"canceled_by__trigramme": None,
}
],
"errors": [],
"warnings": {},
"opegroups_to_update": [
{
"id": group.pk,
"amount": str(group.amount),
"is_cof": group.is_cof,
}
],
},
)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("60.75"))
self.article.refresh_from_db()
self.assertEqual(self.article.stock, 20)
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("110.75"))
ws_data = self.receive_msg()
self.assertDictEqual(
ws_data,
{
"type": "kpsul",
"checkouts": [{"id": self.checkout.pk, "balance": Decimal("110.75")}],
"articles": [],
},
)
@mock.patch("django.utils.timezone.now")
def test_edit(self, now_mock):
now_mock.return_value = self.now
group = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[{"type": Operation.EDIT, "amount": Decimal("-10.75")}],
)
operation = group.opes.get()
now_mock.return_value += timedelta(seconds=15)
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
group = OperationGroup.objects.get()
self.assertDictEqual(
group.__dict__,
{
"_state": mock.ANY,
"amount": Decimal("0.00"),
"at": mock.ANY,
"checkout_id": self.checkout.pk,
"comment": "",
"id": mock.ANY,
"is_cof": False,
"on_acc_id": self.account.pk,
"valid_by_id": None,
},
)
operation = Operation.objects.get()
self.assertDictEqual(
operation.__dict__,
{
"_state": mock.ANY,
"addcost_amount": None,
"addcost_for_id": None,
"amount": Decimal("-10.75"),
"article_id": None,
"article_nb": None,
"canceled_at": self.now + timedelta(seconds=15),
"canceled_by_id": None,
"group_id": group.pk,
"id": mock.ANY,
"type": Operation.EDIT,
},
)
self.assertDictEqual(
json_data,
{
"canceled": [
{
"id": operation.id,
# l'encodage des dates en JSON est relou...
"canceled_at": mock.ANY,
"canceled_by__trigramme": None,
}
],
"errors": [],
"warnings": {},
"opegroups_to_update": [
{
"id": group.pk,
"amount": str(group.amount),
"is_cof": group.is_cof,
}
],
},
)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("60.75"))
self.article.refresh_from_db()
self.assertEqual(self.article.stock, 20)
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("100.00"))
ws_data = self.receive_msg()
self.assertDictEqual(
ws_data,
{"type": "kpsul", "checkouts": [], "articles": []},
)
@mock.patch("django.utils.timezone.now")
def test_old_operations(self, now_mock):
kfet_config.set(cancel_duration=timedelta(minutes=10))
user_add_perms(self.users["team"], ["kfet.cancel_old_operations"])
now_mock.return_value = self.now
group = create_operation_group(
at=self.now,
on_acc=self.account,
checkout=self.checkout,
content=[{"type": Operation.WITHDRAW, "amount": Decimal("-10.75")}],
)
operation = group.opes.get()
now_mock.return_value += timedelta(minutes=10, seconds=1)
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
self.assertEqual(len(json_data["canceled"]), 1)
@mock.patch("django.utils.timezone.now")
def test_invalid_old_operations_requires_perm(self, now_mock):
kfet_config.set(cancel_duration=timedelta(minutes=10))
now_mock.return_value = self.now
group = create_operation_group(
at=self.now,
on_acc=self.account,
checkout=self.checkout,
content=[{"type": Operation.WITHDRAW, "amount": Decimal("-10.75")}],
)
operation = group.opes.get()
now_mock.return_value += timedelta(minutes=10, seconds=1)
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 403)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertEqual(
json_data["missing_perms"],
["Annuler des commandes non récentes"],
)
def test_already_canceled(self):
group = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[
{
"type": Operation.WITHDRAW,
"amount": Decimal("-10.75"),
"canceled_at": timezone.now(),
}
],
)
operation = group.opes.get()
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
self.assertDictEqual(
json_data["warnings"], {"already_canceled": [operation.pk]}
)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("50.00"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("100.00"))
@mock.patch("django.utils.timezone.now")
def test_checkout_before_last_statement(self, now_mock):
now_mock.return_value = self.now
group = create_operation_group(
at=self.now,
on_acc=self.account,
checkout=self.checkout,
content=[{"type": Operation.WITHDRAW, "amount": Decimal("-10.75")}],
)
operation = group.opes.get()
now_mock.return_value += timedelta(seconds=30)
create_checkout_statement(checkout=self.checkout)
now_mock.return_value += timedelta(seconds=30)
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
self.assertEqual(len(json_data["canceled"]), 1)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("60.75"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("100.00"))
@mock.patch("django.utils.timezone.now")
def test_article_before_last_inventory(self, now_mock):
now_mock.return_value = self.now
group = create_operation_group(
at=self.now,
on_acc=self.account,
checkout=self.checkout,
content=[
{"type": Operation.PURCHASE, "article": self.article, "article_nb": 2}
],
)
operation = group.opes.get()
now_mock.return_value += timedelta(seconds=30)
create_inventory_article(article=self.article)
now_mock.return_value += timedelta(seconds=30)
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
self.assertEqual(len(json_data["canceled"]), 1)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("55.00"))
self.article.refresh_from_db()
self.assertEqual(self.article.stock, 20)
def test_negative(self):
kfet_config.set(overdraft_amount=Decimal("40.00"))
user_add_perms(self.users["team"], ["kfet.perform_negative_operations"])
self.account.balance = Decimal("-20.00")
self.account.save()
group = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[{"type": Operation.DEPOSIT, "amount": Decimal("10.75")}],
)
operation = group.opes.get()
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
self.assertEqual(len(json_data["canceled"]), 1)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("-30.75"))
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("89.25"))
def test_invalid_negative_above_thresholds(self):
kfet_config.set(overdraft_amount=Decimal("5.00"))
user_add_perms(self.users["team"], ["kfet.perform_negative_operations"])
self.account.balance = Decimal("-20.00")
self.account.save()
group = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[{"type": Operation.DEPOSIT, "amount": Decimal("10.75")}],
)
operation = group.opes.get()
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 400)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertCountEqual(
[e["code"] for e in json_data["errors"]],
["negative"],
)
def test_invalid_negative_requires_perms(self):
kfet_config.set(overdraft_amount=Decimal("40.00"))
self.account.balance = Decimal("-20.00")
self.account.save()
group = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[{"type": Operation.DEPOSIT, "amount": Decimal("10.75")}],
)
operation = group.opes.get()
data = {"operations[]": [str(operation.pk)]}
resp = self.client.post(self.url, data)
self.assertEqual(resp.status_code, 403)
json_data = json.loads(resp.content.decode("utf-8"))
self.assertEqual(
json_data["missing_perms"],
["Enregistrer des commandes en négatif"],
)
def test_partial_0(self):
group = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[
{"type": Operation.PURCHASE, "article": self.article, "article_nb": 2},
{"type": Operation.DEPOSIT, "amount": Decimal("10.75")},
{"type": Operation.EDIT, "amount": Decimal("-6.00")},
{
"type": Operation.WITHDRAW,
"amount": Decimal("-10.75"),
"canceled_at": timezone.now(),
},
],
)
operation1 = group.opes.get(type=Operation.PURCHASE)
operation2 = group.opes.get(type=Operation.EDIT)
operation3 = group.opes.get(type=Operation.WITHDRAW)
data = {
"operations[]": [str(operation1.pk), str(operation2.pk), str(operation3.pk)]
}
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
group.refresh_from_db()
self.assertEqual(group.amount, Decimal("10.75"))
self.assertEqual(group.opes.exclude(canceled_at=None).count(), 3)
self.maxDiff = None
self.assertDictEqual(
json_data,
{
"canceled": [
{
"id": operation1.id,
# l'encodage des dates en JSON est relou...
"canceled_at": mock.ANY,
"canceled_by__trigramme": None,
},
{
"id": operation2.id,
# l'encodage des dates en JSON est relou...
"canceled_at": mock.ANY,
"canceled_by__trigramme": None,
},
],
"errors": [],
"warnings": {"already_canceled": [operation3.pk]},
"opegroups_to_update": [
{
"id": group.pk,
"amount": str(group.amount),
"is_cof": group.is_cof,
}
],
},
)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("61.00"))
self.article.refresh_from_db()
self.assertEqual(self.article.stock, 22)
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("100.00"))
def test_multi_0(self):
group1 = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[
{"type": Operation.PURCHASE, "article": self.article, "article_nb": 2},
{"type": Operation.DEPOSIT, "amount": Decimal("10.75")},
{"type": Operation.EDIT, "amount": Decimal("-6.00")},
],
)
operation11 = group1.opes.get(type=Operation.PURCHASE)
group2 = create_operation_group(
on_acc=self.account,
checkout=self.checkout,
content=[
{"type": Operation.PURCHASE, "article": self.article, "article_nb": 5},
{"type": Operation.DEPOSIT, "amount": Decimal("3.00")},
],
)
operation21 = group2.opes.get(type=Operation.PURCHASE)
operation22 = group2.opes.get(type=Operation.DEPOSIT)
data = {
"operations[]": [
str(operation11.pk),
str(operation21.pk),
str(operation22.pk),
]
}
resp = self.client.post(self.url, data)
json_data = self._assertResponseOk(resp)
group1.refresh_from_db()
self.assertEqual(group1.amount, Decimal("4.75"))
self.assertEqual(group1.opes.exclude(canceled_at=None).count(), 1)
group2.refresh_from_db()
self.assertEqual(group2.amount, Decimal(0))
self.assertEqual(group2.opes.exclude(canceled_at=None).count(), 2)
self.assertEqual(len(json_data["canceled"]), 3)
self.account.refresh_from_db()
self.assertEqual(self.account.balance, Decimal("64.50"))
self.article.refresh_from_db()
self.assertEqual(self.article.stock, 27)
self.checkout.refresh_from_db()
self.assertEqual(self.checkout.balance, Decimal("97.00"))
class KPsulArticlesData(ViewTestCaseMixin, TestCase):
url_name = "kfet.kpsul.articles_data"
url_expected = "/k-fet/k-psul/articles_data"
auth_user = "team"
auth_forbidden = [None, "user"]
def setUp(self):
super().setUp()
category = ArticleCategory.objects.create(name="Catégorie")
self.article1 = Article.objects.create(category=category, name="Article 1")
self.article2 = Article.objects.create(
category=category, name="Article 2", price=Decimal("2.5")
)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
content = json.loads(r.content.decode("utf-8"))
articles = content["articles"]
expected_list = [
{"category__name": "Catégorie", "name": "Article 1", "price": "0.00"},
{"category__name": "Catégorie", "name": "Article 2", "price": "2.50"},
]
for expected, article in zip(expected_list, articles):
self.assertDictContainsSubset(expected, article)
self.assertSetEqual(
set(article.keys()),
set(
[
"id",
"name",
"price",
"stock",
"category_id",
"category__name",
"category__has_addcost",
"category__has_reduction",
]
),
)
class KPsulUpdateAddcost(ViewTestCaseMixin, TestCase):
url_name = "kfet.kpsul.update_addcost"
url_expected = "/k-fet/k-psul/update_addcost"
http_methods = ["POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
post_data = {"trigramme": "000", "amount": "0.5"}
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.manage_addcosts"])}
def test_ok(self):
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
self.assertEqual(r.status_code, 200)
self.assertEqual(kfet_config.addcost_for, Account.objects.get(trigramme="000"))
self.assertEqual(kfet_config.addcost_amount, Decimal("0.5"))
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbidden(r)
class KPsulGetSettings(ViewTestCaseMixin, TestCase):
url_name = "kfet.kpsul.get_settings"
url_expected = "/k-fet/k-psul/get_settings"
auth_user = "team"
auth_forbidden = [None, "user"]
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
class HistoryJSONViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.history.json"
url_expected = "/k-fet/history.json"
auth_user = "team"
auth_forbidden = [None, "user", "noaccount"]
def test_ok(self):
r = self.client.post(self.url)
self.assertEqual(r.status_code, 200)
def get_users_extra(self):
noaccount = User.objects.create(username="noaccount")
noaccount.set_password("noaccount")
noaccount.save()
return {"noaccount": noaccount}
class AccountReadJSONViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.account.read.json"
http_methods = ["GET"]
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"trigramme": self.accounts["user"].trigramme}
@property
def url_expected(self):
return "/k-fet/accounts/{}/.json".format(self.accounts["user"].trigramme)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
content = json.loads(r.content.decode("utf-8"))
expected = {"name": "first last", "trigramme": "000", "balance": "0.00"}
self.assertDictContainsSubset(expected, content)
self.assertSetEqual(
set(content.keys()),
set(
[
"balance",
"departement",
"email",
"id",
"is_cof",
"is_frozen",
"name",
"nickname",
"promo",
"trigramme",
]
),
)
class SettingsListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.settings"
url_expected = "/k-fet/settings/"
auth_user = "team1"
auth_forbidden = [None, "user", "team"]
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.see_config"])}
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
class SettingsUpdateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.settings.update"
url_expected = "/k-fet/settings/edit"
http_methods = ["GET", "POST"]
auth_user = "team1"
auth_forbidden = [None, "user", "team"]
@property
def post_data(self):
return {
"kfet_reduction_cof": "25",
"kfet_addcost_amount": "0.5",
"kfet_addcost_for": self.accounts["user"].pk,
"kfet_overdraft_duration": "2 00:00:00",
"kfet_overdraft_amount": "25",
"kfet_cancel_duration": "00:20:00",
}
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.change_config"])}
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
r = self.client.post(self.url, self.post_data)
# Redirect is skipped because client may lack permissions.
self.assertRedirects(r, reverse("kfet.settings"), fetch_redirect_response=False)
expected_config = {
"reduction_cof": Decimal("25"),
"addcost_amount": Decimal("0.5"),
"addcost_for": self.accounts["user"],
"overdraft_duration": timedelta(days=2),
"overdraft_amount": Decimal("25"),
"cancel_duration": timedelta(minutes=20),
}
for key, expected in expected_config.items():
self.assertEqual(getattr(kfet_config, key), expected)
class TransferListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.transfers"
url_expected = "/k-fet/transfers/"
auth_user = "team"
auth_forbidden = [None, "user"]
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
class TransferCreateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.transfers.create"
url_expected = "/k-fet/transfers/new"
auth_user = "team"
auth_forbidden = [None, "user"]
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
class TransferPerformViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.transfers.perform"
url_expected = "/k-fet/transfers/perform"
http_methods = ["POST"]
auth_user = "team1"
auth_forbidden = [None, "user", "team"]
def get_users_extra(self):
return {
"team1": create_team(
"team1",
"101",
perms=[
# Required
"kfet.add_transfer",
# Convenience
"kfet.perform_negative_operations",
],
)
}
@property
def post_data(self):
return {
# General
"comment": "",
# Formset management
"form-TOTAL_FORMS": "10",
"form-INITIAL_FORMS": "0",
"form-MIN_NUM_FORMS": "1",
"form-MAX_NUM_FORMS": "1000",
# Transfer 1
"form-0-from_acc": str(self.accounts["user"].pk),
"form-0-to_acc": str(self.accounts["team"].pk),
"form-0-amount": "3.5",
# Transfer 2
"form-1-from_acc": str(self.accounts["team"].pk),
"form-1-to_acc": str(self.accounts["team1"].pk),
"form-1-amount": "2.4",
}
def test_ok(self):
r = self.client.post(self.url, self.post_data)
self.assertEqual(r.status_code, 200)
user = self.accounts["user"]
user.refresh_from_db()
self.assertEqual(user.balance, Decimal("-3.5"))
team = self.accounts["team"]
team.refresh_from_db()
self.assertEqual(team.balance, Decimal("1.1"))
team1 = self.accounts["team1"]
team1.refresh_from_db()
self.assertEqual(team1.balance, Decimal("2.4"))
class TransferCancelViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.transfers.cancel"
url_expected = "/k-fet/transfers/cancel"
http_methods = ["POST"]
auth_user = "team1"
auth_forbidden = [None, "user", "team"]
def get_users_extra(self):
return {
"team1": create_team(
"team1",
"101",
perms=[
# Convenience
"kfet.perform_negative_operations"
],
)
}
@property
def post_data(self):
return {"transfers[]": [self.transfer1.pk, self.transfer2.pk]}
def setUp(self):
super().setUp()
group = TransferGroup.objects.create()
self.transfer1 = Transfer.objects.create(
group=group,
from_acc=self.accounts["user"],
to_acc=self.accounts["team"],
amount="3.5",
)
self.transfer2 = Transfer.objects.create(
group=group,
from_acc=self.accounts["team"],
to_acc=self.accounts["root"],
amount="2.4",
)
def test_ok(self):
r = self.client.post(self.url, self.post_data)
self.assertEqual(r.status_code, 200)
user = self.accounts["user"]
user.refresh_from_db()
self.assertEqual(user.balance, Decimal("3.5"))
team = self.accounts["team"]
team.refresh_from_db()
self.assertEqual(team.balance, Decimal("-1.1"))
root = self.accounts["root"]
root.refresh_from_db()
self.assertEqual(root.balance, Decimal("-2.4"))
class InventoryListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.inventory"
url_expected = "/k-fet/inventaires/"
auth_user = "team"
auth_forbidden = [None, "user"]
def setUp(self):
super().setUp()
self.inventory = Inventory.objects.create(by=self.accounts["team"])
category = ArticleCategory.objects.create(name="Category")
article = Article.objects.create(name="Article", category=category)
InventoryArticle.objects.create(
inventory=self.inventory, article=article, stock_old=5, stock_new=0
)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
inventories = r.context["inventories"]
self.assertQuerysetEqual(inventories, map(repr, [self.inventory]))
class InventoryCreateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.inventory.create"
url_expected = "/k-fet/inventaires/new"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.add_inventory"])}
@property
def post_data(self):
return {
# Formset management
"form-TOTAL_FORMS": "2",
"form-INITIAL_FORMS": "2",
"form-MIN_NUM_FORMS": "0",
"form-MAX_NUM_FORMS": "1000",
# Article 1
"form-0-article": str(self.article1.pk),
"form-0-stock_new": "5",
# Article 2
"form-1-article": str(self.article2.pk),
"form-1-stock_new": "10",
}
def setUp(self):
super().setUp()
category = ArticleCategory.objects.create(name="Category")
self.article1 = Article.objects.create(category=category, name="Article 1")
self.article2 = Article.objects.create(category=category, name="Article 2")
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
self.assertRedirects(r, reverse("kfet.inventory"))
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)
class InventoryReadViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.inventory.read"
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"pk": self.inventory.pk}
@property
def url_expected(self):
return "/k-fet/inventaires/{}".format(self.inventory.pk)
def setUp(self):
super().setUp()
self.inventory = Inventory.objects.create(by=self.accounts["team"])
category = ArticleCategory.objects.create(name="Category")
article = Article.objects.create(name="Article", category=category)
InventoryArticle.objects.create(
inventory=self.inventory, article=article, stock_old=5, stock_new=0
)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
class InventoryDeleteViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.inventory.delete"
auth_user = "team1"
auth_forbidden = [None, "user", "team"]
def get_users_extra(self):
return {
"user1": create_user("user1", "001"),
"team1": create_team("team1", "101", perms=["kfet.delete_inventory"]),
}
@property
def url_kwargs(self):
return {"pk": self.inventory1.pk}
@property
def url_expected(self):
return "/k-fet/inventaires/{}/delete".format(self.inventory1.pk)
def setUp(self):
super().setUp()
# Deux inventaires : un avec article 1 + 2, l'autre avec 1 + 3
self.inventory1 = Inventory.objects.create(
by=self.accounts["team"], at=self.now
)
self.inventory2 = Inventory.objects.create(
by=self.accounts["team"], at=self.now + timedelta(days=1)
)
category = ArticleCategory.objects.create(name="Category")
# Le stock des articles correspond à leur dernier inventaire
self.article1 = Article.objects.create(
name="Article1", category=category, stock=51
)
self.article2 = Article.objects.create(
name="Article2", category=category, stock=42
)
self.article3 = Article.objects.create(
name="Article3", category=category, stock=42
)
InventoryArticle.objects.create(
inventory=self.inventory1,
article=self.article1,
stock_old=23,
stock_new=42,
)
InventoryArticle.objects.create(
inventory=self.inventory1,
article=self.article2,
stock_old=23,
stock_new=42,
)
InventoryArticle.objects.create(
inventory=self.inventory2,
article=self.article1,
stock_old=42,
stock_new=51,
)
InventoryArticle.objects.create(
inventory=self.inventory2,
article=self.article3,
stock_old=23,
stock_new=42,
)
def test_ok(self):
r = self.client.post(self.url)
self.assertRedirects(r, reverse("kfet.inventory"))
# On vérifie que l'inventaire n'existe plus
self.assertFalse(Inventory.objects.filter(pk=self.inventory1.pk).exists())
# On check les stocks
self.article1.refresh_from_db()
self.article2.refresh_from_db()
self.article3.refresh_from_db()
self.assertEqual(self.article1.stock, 51)
self.assertEqual(self.article2.stock, 23)
self.assertEqual(self.article3.stock, 42)
class OrderListViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.order"
url_expected = "/k-fet/orders/"
auth_user = "team"
auth_forbidden = [None, "user"]
def setUp(self):
super().setUp()
category = ArticleCategory.objects.create(name="Category")
article = Article.objects.create(name="Article", category=category)
supplier = Supplier.objects.create(name="Supplier")
SupplierArticle.objects.create(supplier=supplier, article=article)
self.order = Order.objects.create(supplier=supplier)
OrderArticle.objects.create(
order=self.order, article=article, quantity_ordered=24
)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
orders = r.context["orders"]
self.assertQuerysetEqual(orders, map(repr, [self.order]))
class OrderReadViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.order.read"
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"pk": self.order.pk}
@property
def url_expected(self):
return "/k-fet/orders/{}".format(self.order.pk)
def setUp(self):
super().setUp()
category = ArticleCategory.objects.create(name="Category")
article = Article.objects.create(name="Article", category=category)
supplier = Supplier.objects.create(name="Supplier")
SupplierArticle.objects.create(supplier=supplier, article=article)
self.order = Order.objects.create(supplier=supplier)
OrderArticle.objects.create(
order=self.order, article=article, quantity_ordered=24
)
def test_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
class SupplierUpdateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.order.supplier.update"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"pk": self.supplier.pk}
@property
def url_expected(self):
return "/k-fet/orders/suppliers/{}/edit".format(self.supplier.pk)
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.change_supplier"])}
@property
def post_data(self):
return {
"name": "The Supplier",
"phone": "",
"comment": "",
"address": "",
"email": "",
}
def setUp(self):
super().setUp()
self.supplier = Supplier.objects.create(name="Supplier")
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
def test_post_ok(self):
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
self.assertRedirects(r, reverse("kfet.order"))
self.supplier.refresh_from_db()
self.assertEqual(self.supplier.name, "The Supplier")
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)
class OrderCreateViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.order.new"
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"pk": self.supplier.pk}
@property
def url_expected(self):
return "/k-fet/orders/suppliers/{}/new-order".format(self.supplier.pk)
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.add_order"])}
@property
def post_data(self):
return {
# Formset management
"form-TOTAL_FORMS": "1",
"form-INITIAL_FORMS": "1",
"form-MIN_NUM_FORMS": "0",
"form-MAX_NUM_FORMS": "1000",
# Article
"form-0-article": self.article.pk,
"form-0-quantity_ordered": "20",
}
def setUp(self):
super().setUp()
category = ArticleCategory.objects.create(name="Category")
self.article = Article.objects.create(name="Article", category=category)
self.supplier = Supplier.objects.create(name="Supplier")
SupplierArticle.objects.create(supplier=self.supplier, article=self.article)
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
@mock.patch("django.utils.timezone.now")
def test_post_ok(self, mock_now):
mock_now.return_value = self.now
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
order = Order.objects.get(at=self.now)
self.assertRedirects(r, reverse("kfet.order.read", args=[order.pk]))
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)
class OrderToInventoryViewTests(ViewTestCaseMixin, TestCase):
url_name = "kfet.order.to_inventory"
http_methods = ["GET", "POST"]
auth_user = "team"
auth_forbidden = [None, "user"]
@property
def url_kwargs(self):
return {"pk": self.order.pk}
@property
def url_expected(self):
return "/k-fet/orders/{}/to_inventory".format(self.order.pk)
def get_users_extra(self):
return {"team1": create_team("team1", "101", perms=["kfet.order_to_inventory"])}
@property
def post_data(self):
return {
# Formset mangaement
"form-TOTAL_FORMS": "1",
"form-INITIAL_FORMS": "1",
"form-MIN_NUM_FORMS": "0",
"form-MAX_NUM_FORMS": "1000",
# Article 1
"form-0-article": self.article.pk,
"form-0-quantity_received": "20",
"form-0-price_HT": "",
"form-0-TVA": "",
"form-0-rights": "",
}
def setUp(self):
super().setUp()
category = ArticleCategory.objects.create(name="Category")
self.article = Article.objects.create(name="Article", category=category)
supplier = Supplier.objects.create(name="Supplier")
SupplierArticle.objects.create(supplier=supplier, article=self.article)
self.order = Order.objects.create(supplier=supplier)
OrderArticle.objects.create(
order=self.order, article=self.article, quantity_ordered=24
)
def test_get_ok(self):
r = self.client.get(self.url)
self.assertEqual(r.status_code, 200)
@mock.patch("django.utils.timezone.now")
def test_post_ok(self, mock_now):
mock_now.return_value = self.now
client = Client()
client.login(username="team1", password="team1")
r = client.post(self.url, self.post_data)
self.assertRedirects(r, reverse("kfet.order"))
inventory = Inventory.objects.first()
self.assertInstanceExpected(
inventory,
{"by": self.accounts["team1"], "at": self.now, "order": self.order},
)
self.assertQuerysetEqual(inventory.articles.all(), map(repr, [self.article]))
compte = InventoryArticle.objects.get(article=self.article)
self.assertInstanceExpected(
compte, {"stock_old": 0, "stock_new": 20, "stock_error": 0}
)
def test_post_forbidden(self):
r = self.client.post(self.url, self.post_data)
self.assertForbiddenKfet(r)