Skip to content

Commit

Permalink
feat(Prices): new field to allow users to input the quantity bought (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
raphodn authored Oct 18, 2024
1 parent 8a9aa50 commit 857b813
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 34 deletions.
2 changes: 2 additions & 0 deletions open_prices/api/prices/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ def setUpTestData(cls):
)
cls.user_price = PriceFactory(
**PRICE_8001505005707,
receipt_quantity=2,
proof_id=cls.user_proof.id,
owner=cls.user_session.user.user_id,
)
Expand Down Expand Up @@ -377,6 +378,7 @@ def test_price_create(self):
self.assertEqual(response.data["price"], 15.00)
self.assertEqual(response.data["currency"], "EUR")
self.assertEqual(response.data["date"], "2024-01-01")
self.assertEqual(response.data["receipt_quantity"], 1) # default
self.assertTrue("source" not in response.data)
self.assertEqual(response.data["owner"], self.user_session.user.user_id)
# with proof, product & location
Expand Down
23 changes: 23 additions & 0 deletions open_prices/prices/migrations/0003_price_receipt_quantity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 5.1 on 2024-10-18 20:51

import django.core.validators
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("prices", "0002_alter_price_currency"),
]

operations = [
migrations.AddField(
model_name="price",
name="receipt_quantity",
field=models.PositiveIntegerField(
blank=True,
null=True,
validators=[django.core.validators.MinValueValidator(1)],
verbose_name="Receipt's price quantity (user input)",
),
),
]
19 changes: 19 additions & 0 deletions open_prices/prices/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class Price(models.Model):
"price_per",
"currency",
"date",
"receipt_quantity",
]
CREATE_FIELDS = UPDATE_FIELDS + [
"product_code",
Expand Down Expand Up @@ -133,6 +134,13 @@ class Price(models.Model):
related_name="prices",
)

receipt_quantity = models.PositiveIntegerField(
verbose_name="Receipt's price quantity (user input)",
validators=[MinValueValidator(1)],
blank=True,
null=True,
)

owner = models.CharField(blank=True, null=True)
source = models.CharField(blank=True, null=True)

Expand Down Expand Up @@ -328,6 +336,7 @@ def clean(self, *args, **kwargs):
# proof rules
# - proof must exist and belong to the price owner
# - some proof fields should be the same as the price fields
# - receipt_quantity can only be set for receipts (default to 1)
if self.proof_id:
from open_prices.proofs.models import Proof

Expand Down Expand Up @@ -361,6 +370,16 @@ def clean(self, *args, **kwargs):
"proof",
f"Proof {PROOF_FIELD} ({proof_field_value}) does not match the price {PROOF_FIELD} ({price_field_value})",
)
if proof.type in proof_constants.TYPE_SHOPPING_SESSION_LIST:
if not self.receipt_quantity:
self.receipt_quantity = 1
else:
if self.receipt_quantity is not None:
validation_errors = utils.add_validation_error(
validation_errors,
"receipt_quantity",
f"Can only be set if proof type in {proof_constants.TYPE_SHOPPING_SESSION_LIST}",
)
# return
if bool(validation_errors):
raise ValidationError(validation_errors)
Expand Down
93 changes: 59 additions & 34 deletions open_prices/prices/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def test_price_location_validation(self):

def test_price_proof_validation(self):
self.user_session = SessionFactory()
self.user_proof = ProofFactory(
self.user_proof_receipt = ProofFactory(
type=proof_constants.TYPE_RECEIPT,
location_osm_id=652825274,
location_osm_type=location_constants.OSM_TYPE_NODE,
Expand All @@ -288,69 +288,94 @@ def test_price_proof_validation(self):
)
self.proof_2 = ProofFactory()
# proof not set
PriceFactory(proof=None, owner=self.user_proof.owner)
PriceFactory(proof=None, owner=self.user_proof_receipt.owner)
# same price & proof fields
PriceFactory(
proof=self.user_proof,
location_osm_id=self.user_proof.location_osm_id,
location_osm_type=self.user_proof.location_osm_type,
date=self.user_proof.date,
currency=self.user_proof.currency,
owner=self.user_proof.owner,
proof=self.user_proof_receipt,
location_osm_id=self.user_proof_receipt.location_osm_id,
location_osm_type=self.user_proof_receipt.location_osm_type,
date=self.user_proof_receipt.date,
currency=self.user_proof_receipt.currency,
owner=self.user_proof_receipt.owner,
)
# different price & proof owner
self.assertRaises(
ValidationError,
PriceFactory,
proof=self.proof_2, # different
location_osm_id=self.user_proof.location_osm_id,
location_osm_type=self.user_proof.location_osm_type,
date=self.user_proof.date,
currency=self.user_proof.currency,
owner=self.user_proof.owner,
location_osm_id=self.user_proof_receipt.location_osm_id,
location_osm_type=self.user_proof_receipt.location_osm_type,
date=self.user_proof_receipt.date,
currency=self.user_proof_receipt.currency,
owner=self.user_proof_receipt.owner,
)
# proof location_osm_id & location_osm_type
self.assertRaises(
ValidationError,
PriceFactory,
proof=self.user_proof,
proof=self.user_proof_receipt,
location_osm_id=5, # different location_osm_id
location_osm_type=self.user_proof.location_osm_type,
date=self.user_proof.date,
currency=self.user_proof.currency,
owner=self.user_proof.owner,
location_osm_type=self.user_proof_receipt.location_osm_type,
date=self.user_proof_receipt.date,
currency=self.user_proof_receipt.currency,
owner=self.user_proof_receipt.owner,
)
self.assertRaises(
ValidationError,
PriceFactory,
proof=self.user_proof,
location_osm_id=self.user_proof.location_osm_id,
proof=self.user_proof_receipt,
location_osm_id=self.user_proof_receipt.location_osm_id,
location_osm_type="WAY", # different location_osm_type
date=self.user_proof.date,
currency=self.user_proof.currency,
owner=self.user_proof.owner,
date=self.user_proof_receipt.date,
currency=self.user_proof_receipt.currency,
owner=self.user_proof_receipt.owner,
)
# proof date & currency
self.assertRaises(
ValidationError,
PriceFactory,
proof=self.user_proof,
location_osm_id=self.user_proof.location_osm_id,
location_osm_type=self.user_proof.location_osm_type,
proof=self.user_proof_receipt,
location_osm_id=self.user_proof_receipt.location_osm_id,
location_osm_type=self.user_proof_receipt.location_osm_type,
date="2024-07-01", # different date
currency=self.user_proof.currency,
owner=self.user_proof.owner,
currency=self.user_proof_receipt.currency,
owner=self.user_proof_receipt.owner,
)
self.assertRaises(
ValidationError,
PriceFactory,
proof=self.user_proof,
location_osm_id=self.user_proof.location_osm_id,
location_osm_type=self.user_proof.location_osm_type,
date=self.user_proof.date,
proof=self.user_proof_receipt,
location_osm_id=self.user_proof_receipt.location_osm_id,
location_osm_type=self.user_proof_receipt.location_osm_type,
date=self.user_proof_receipt.date,
currency="USD", # different currency
owner=self.user_proof.owner,
)
owner=self.user_proof_receipt.owner,
)
# receipt_quantity
for RECEIPT_QUANTITY_NOT_OK in [-5, 0]:
with self.subTest(RECEIPT_QUANTITY_NOT_OK=RECEIPT_QUANTITY_NOT_OK):
self.assertRaises(
ValidationError,
PriceFactory,
proof=self.user_proof_receipt,
location_osm_id=self.user_proof_receipt.location_osm_id,
location_osm_type=self.user_proof_receipt.location_osm_type,
date=self.user_proof_receipt.date,
currency=self.user_proof_receipt.currency,
owner=self.user_proof_receipt.owner,
receipt_quantity=RECEIPT_QUANTITY_NOT_OK,
)
for RECEIPT_QUANTITY_OK in [None, 1, 2]:
with self.subTest(RECEIPT_QUANTITY_OK=RECEIPT_QUANTITY_OK):
PriceFactory(
proof=self.user_proof_receipt,
location_osm_id=self.user_proof_receipt.location_osm_id,
location_osm_type=self.user_proof_receipt.location_osm_type,
date=self.user_proof_receipt.date,
currency=self.user_proof_receipt.currency,
owner=self.user_proof_receipt.owner,
receipt_quantity=RECEIPT_QUANTITY_OK,
)

def test_price_count_increment(self):
user_session = SessionFactory()
Expand Down
1 change: 1 addition & 0 deletions open_prices/proofs/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@

# 1 proof = 1 shop + 1 date
TYPE_SINGLE_SHOP_LIST = [TYPE_PRICE_TAG, TYPE_RECEIPT, TYPE_SHOP_IMPORT]
TYPE_SHOPPING_SESSION_LIST = [TYPE_RECEIPT, TYPE_GDPR_REQUEST]
TYPE_MULTIPLE_SHOP_LIST = [TYPE_GDPR_REQUEST]
3 changes: 3 additions & 0 deletions open_prices/proofs/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ def has_type_shop_import(self):
def has_type_single_shop(self):
return self.filter(type__in=proof_constants.TYPE_SINGLE_SHOP_LIST)

def has_type_shopping_session(self):
return self.filter(type=proof_constants.TYPE_SHOPPING_SESSION_LIST)

def has_prices(self):
return self.filter(price_count__gt=0)

Expand Down

0 comments on commit 857b813

Please sign in to comment.