Skip to content

Commit

Permalink
Add a helper to approximate receipt dates
Browse files Browse the repository at this point in the history
  • Loading branch information
WhyNotHugo committed Nov 16, 2023
1 parent e2a0b07 commit e32e5fa
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 0 deletions.
53 changes: 53 additions & 0 deletions django_afip/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1336,6 +1336,59 @@ def revalidate(self) -> ReceiptValidation | None:
return validation
return None

def approximate_date(receipt: models.Receipt) -> bool:
"""Approximate the date of the receipt as close as possible.
If a receipt should have been validated in a past date, adjust its date as close
as possible:
- Receipts can only be validated with dates as far as 14 days ago. If the
receipt date is older than that, set it to 14 days ago.
- If other receipts have been validated on a more recent date, the receipt
cannot be older than the most recent one.
If the ``issued_date`` needs to be changed, the field in the input receipt will
be updated and atomically saved to the database.
Returns ``True`` if the date has been changed.
"""
today = datetime.now(TZ_AR).date()

if receipt.issued_date == today:
return False

most_recent = (
Receipt.objects.filter(
point_of_sales=receipt.point_of_sales,
receipt_type=receipt.receipt_type,
validation__result=ReceiptValidation.RESULT_APPROVED,
)
.order_by("issued_date")
.last()
)

fortnight_ago = today - timedelta(days=14)
if most_recent is not None:
oldest_possible = max(most_recent.issued_date, fortnight_ago)
else:
oldest_possible = fortnight_ago

if receipt.issued_date >= oldest_possible:
return False

# Commit this atomically to avoid race conditions.
Receipt.objects.filter(
pk=receipt.id,
receipt_number__isnull=True,
).update(
issued_date=oldest_possible,
)

# Mutate the input object to avoid inconsistency issues.
receipt.issued_date = oldest_possible

return True

def __repr__(self) -> str:
return "<Receipt {}: {} {} for {}>".format(
self.pk,
Expand Down
3 changes: 3 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ acá.
- **BREAKING**: The signal that auto-generated receipt pdfs for validated
ReceiptPDFs has been removed. Applications now need to explicitly call
:meth:`~.ReceiptPDF.save_pdf()`.
- Add a new helper helper method :meth:`~.Receipt.approximate_date`. It is
intended to be used to automatically approximate dates on systems which
perform automatic or unattended receipt validation.

11.3.1
------
Expand Down
103 changes: 103 additions & 0 deletions tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from __future__ import annotations

from datetime import date
from datetime import datetime
from datetime import timedelta
from decimal import Decimal
from typing import TYPE_CHECKING
from unittest.mock import MagicMock
Expand All @@ -8,11 +11,13 @@

import pytest
from django.db.models import DecimalField
from freezegun import freeze_time
from pytest_django.asserts import assertQuerysetEqual

from django_afip import exceptions
from django_afip import factories
from django_afip import models
from django_afip.clients import TZ_AR
from django_afip.factories import ReceiptFactory
from django_afip.factories import ReceiptFCEAWithVatAndTaxFactory
from django_afip.factories import ReceiptFCEAWithVatTaxAndOptionalsFactory
Expand Down Expand Up @@ -456,3 +461,101 @@ def test_receipt_entry_manage_decimal_quantities() -> None:
last = models.ReceiptEntry.objects.last()
assert last is not None
assert last.quantity == Decimal("5.23")


@pytest.mark.django_db()
@freeze_time("2023-11-16 18:39:40")
def test_approximate_noop_today() -> None:
today = datetime.now(TZ_AR).date()

factories.ReceiptWithApprovedValidation(issued_date=today - timedelta(days=20))

receipt = factories.ReceiptFactory(issued_date=today)
changed = receipt.approximate_date()

assert changed is False
assert receipt.issued_date == today


@pytest.mark.django_db()
@freeze_time("2023-11-16 18:39:40")
def test_approximate_noop_two_days_ago() -> None:
today = datetime.now(TZ_AR).date()
two_days_ago = date(2023, 11, 14)

factories.ReceiptWithApprovedValidation(issued_date=today - timedelta(days=20))

receipt = factories.ReceiptFactory(issued_date=two_days_ago)
changed = receipt.approximate_date()

assert changed is False
assert receipt.issued_date == two_days_ago


@pytest.mark.django_db()
@freeze_time("2023-11-16 18:39:40")
def test_approximate_date_today_with_most_recent() -> None:
today = datetime.now(TZ_AR).date()

factories.ReceiptWithApprovedValidation(issued_date=today)

receipt = factories.ReceiptFactory(issued_date=today - timedelta(days=30))
changed = receipt.approximate_date()

assert changed is True
assert receipt.issued_date == date(2023, 11, 16)


@pytest.mark.django_db()
@freeze_time("2023-11-16 18:39:40")
def test_approximate_date_yesterday_with_most_recent() -> None:
today = datetime.now(TZ_AR).date()

factories.ReceiptWithApprovedValidation(issued_date=today - timedelta(days=1))

receipt = factories.ReceiptFactory(issued_date=today - timedelta(days=30))
changed = receipt.approximate_date()

assert changed is True
assert receipt.issued_date == date(2023, 11, 15)


@pytest.mark.django_db()
@freeze_time("2023-11-16 18:39:40")
def test_approximate_date_30_days_ago_with_most_recent_20_days_ago() -> None:
today = datetime.now(TZ_AR).date()

factories.ReceiptWithApprovedValidation(issued_date=today - timedelta(days=20))

receipt = factories.ReceiptFactory(issued_date=today - timedelta(days=30))
changed = receipt.approximate_date()

assert changed is True
assert receipt.issued_date == date(2023, 11, 2)


@pytest.mark.django_db()
@freeze_time("2023-11-16 18:39:40")
def test_approximate_date_2_days_ago_with_most_recent_20_days_ago() -> None:
today = datetime.now(TZ_AR).date()
two_days_ago = date(2023, 11, 14)

factories.ReceiptWithApprovedValidation(issued_date=today - timedelta(days=20))

receipt = factories.ReceiptFactory(issued_date=two_days_ago)
changed = receipt.approximate_date()

assert changed is False
assert receipt.issued_date == two_days_ago


@pytest.mark.django_db()
@freeze_time("2023-11-16 18:39:40")
def test_approximate_date_two_days_ago_without_most_recent() -> None:
two_days_ago = date(2023, 11, 14)

receipt = factories.ReceiptFactory(issued_date=two_days_ago)
changed = receipt.approximate_date()

assert changed is False
assert receipt.issued_date == two_days_ago

0 comments on commit e32e5fa

Please sign in to comment.